Browse Source

web: AttrsScope interface extraction (#1694)

* AttrsScope interface extraction

* PR amendments

* resolve conflicts

* remove yarn lockfile

* move submodule back
pull/1777/head
Martynas Petuška 3 years ago committed by GitHub
parent
commit
f98dfbc761
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 5
      .gitignore
  2. 4
      web/benchmark-core/build.gradle.kts
  3. 20
      web/build.gradle.kts
  4. 186
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt
  5. 148
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/AttrsScope.kt
  6. 232
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/EventsListenerScope.kt
  7. 6
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/SyntheticEventListener.kt
  8. 37
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/InputAttrsScope.kt
  9. 18
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/SelectAttrsScope.kt
  10. 49
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/TextAreaAttrsBuilder.kt
  11. 64
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/TextAreaAttrsScope.kt
  12. 2
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSRules.kt
  13. 23
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleScope.kt
  14. 21
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Base.kt
  15. 153
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Elements.kt
  16. 42
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/InputElements.kt
  17. 4
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/RadioGroup.kt
  18. 42
      web/core/src/jsTest/kotlin/elements/AttributesTests.kt
  19. 7
      web/core/src/jsTest/kotlin/elements/ElementsTests.kt
  20. 2
      web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/tests/InputsTests.kt
  21. 258
      web/svg/src/jsMain/kotlin/org/jetbrains/compose/web/svg/svg.kt
  22. 8
      web/widgets/src/jsMain/kotlin/Modifier.kt
  23. 6
      web/widgets/src/jsMain/kotlin/internal/ActualModifier.kt

5
.gitignore vendored

@ -15,4 +15,7 @@ out
build/ build/
/captures /captures
.externalNativeBuild .externalNativeBuild
.cxx .cxx
# Ignoring the persistant lockfile until kotlin.js vulnerabilities are fixed
yarn.lock

4
web/benchmark-core/build.gradle.kts

@ -6,11 +6,11 @@ plugins {
kotlin { kotlin {
js(IR) { js(IR) {
browser() { browser {
testTask { testTask {
useKarma { useKarma {
useChromeHeadless() useChromeHeadless()
//useFirefox() // useFirefox()
} }
} }
} }

20
web/build.gradle.kts

@ -1,4 +1,3 @@
import org.gradle.api.tasks.testing.AbstractTestTask
import org.gradle.api.tasks.testing.logging.TestExceptionFormat import org.gradle.api.tasks.testing.logging.TestExceptionFormat
import org.jetbrains.compose.gradle.kotlinKarmaConfig import org.jetbrains.compose.gradle.kotlinKarmaConfig
@ -19,7 +18,7 @@ fun Project.isSampleProject() = projectDir.parentFile.name == "examples"
tasks.register("printBundleSize") { tasks.register("printBundleSize") {
dependsOn( dependsOn(
subprojects.filter { it.isSampleProject() }.map { ":examples:${it.name}:printBundleSize" } subprojects.filter { it.isSampleProject() }.map { ":examples:${it.name}:printBundleSize" }
) )
} }
@ -34,7 +33,7 @@ subprojects {
group = "org.jetbrains.compose.web" group = "org.jetbrains.compose.web"
version = COMPOSE_WEB_VERSION version = COMPOSE_WEB_VERSION
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() { tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "11" kotlinOptions.jvmTarget = "11"
} }
@ -54,6 +53,16 @@ subprojects {
} }
pluginManager.withPlugin("kotlin-multiplatform") { pluginManager.withPlugin("kotlin-multiplatform") {
configure<org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension> {
sourceSets {
all {
languageSettings {
optIn("org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi")
optIn("kotlin.RequiresOptIn")
}
}
}
}
val printTestBundleSize by tasks.registering { val printTestBundleSize by tasks.registering {
dependsOn(tasks.named("jsTest")) dependsOn(tasks.named("jsTest"))
doLast { doLast {
@ -72,7 +81,6 @@ subprojects {
} }
} }
if (isSampleProject()) { if (isSampleProject()) {
val printBundleSize by tasks.registering { val printBundleSize by tasks.registering {
dependsOn(tasks.named("jsBrowserDistribution")) dependsOn(tasks.named("jsBrowserDistribution"))
@ -93,10 +101,10 @@ subprojects {
configurations.all { configurations.all {
resolutionStrategy.dependencySubstitution { resolutionStrategy.dependencySubstitution {
substitute(module("org.jetbrains.compose.web:web-widgets")).apply { substitute(module("org.jetbrains.compose.web:web-widgets")).apply {
with(project(":web-widgets")) with(project(":web-widgets"))
} }
substitute(module("org.jetbrains.compose.web:web-core")).apply { substitute(module("org.jetbrains.compose.web:web-core")).apply {
with(project(":web-core")) with(project(":web-core"))
} }
} }
} }

186
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/Attrs.kt

@ -15,284 +15,284 @@ import org.w3c.dom.HTMLTableCellElement
import org.w3c.dom.HTMLTableColElement import org.w3c.dom.HTMLTableColElement
import org.w3c.dom.HTMLTextAreaElement import org.w3c.dom.HTMLTextAreaElement
fun AttrsBuilder<HTMLAnchorElement>.href(value: String) = fun AttrsScope<HTMLAnchorElement>.href(value: String) =
attr("href", value) attr("href", value)
fun AttrsBuilder<HTMLAnchorElement>.target(value: ATarget = ATarget.Self) = fun AttrsScope<HTMLAnchorElement>.target(value: ATarget = ATarget.Self) =
attr("target", value.targetStr) attr("target", value.targetStr)
fun AttrsBuilder<HTMLAnchorElement>.ref(value: ARel) = fun AttrsScope<HTMLAnchorElement>.ref(value: ARel) =
attr("rel", value.relStr) attr("rel", value.relStr)
fun AttrsBuilder<HTMLAnchorElement>.ping(value: String) = fun AttrsScope<HTMLAnchorElement>.ping(value: String) =
attr("ping", value) attr("ping", value)
fun AttrsBuilder<HTMLAnchorElement>.ping(vararg urls: String) = fun AttrsScope<HTMLAnchorElement>.ping(vararg urls: String) =
attr("ping", urls.joinToString(" ")) attr("ping", urls.joinToString(" "))
fun AttrsBuilder<HTMLAnchorElement>.hreflang(value: String) = fun AttrsScope<HTMLAnchorElement>.hreflang(value: String) =
attr("hreflang", value) attr("hreflang", value)
fun AttrsBuilder<HTMLAnchorElement>.download(value: String = "") = fun AttrsScope<HTMLAnchorElement>.download(value: String = "") =
attr("download", value) attr("download", value)
/* Button attributes */ /* Button attributes */
fun AttrsBuilder<HTMLButtonElement>.autoFocus() = fun AttrsScope<HTMLButtonElement>.autoFocus() =
attr("autofocus", "") attr("autofocus", "")
fun AttrsBuilder<HTMLButtonElement>.disabled() = fun AttrsScope<HTMLButtonElement>.disabled() =
attr("disabled", "") attr("disabled", "")
fun AttrsBuilder<HTMLButtonElement>.form(formId: String) = fun AttrsScope<HTMLButtonElement>.form(formId: String) =
attr("form", formId) attr("form", formId)
fun AttrsBuilder<HTMLButtonElement>.formAction(url: String) = fun AttrsScope<HTMLButtonElement>.formAction(url: String) =
attr("formaction", url) attr("formaction", url)
fun AttrsBuilder<HTMLButtonElement>.formEncType(value: ButtonFormEncType) = fun AttrsScope<HTMLButtonElement>.formEncType(value: ButtonFormEncType) =
attr("formenctype", value.typeStr) attr("formenctype", value.typeStr)
fun AttrsBuilder<HTMLButtonElement>.formMethod(value: ButtonFormMethod) = fun AttrsScope<HTMLButtonElement>.formMethod(value: ButtonFormMethod) =
attr("formmethod", value.methodStr) attr("formmethod", value.methodStr)
fun AttrsBuilder<HTMLButtonElement>.formNoValidate() = fun AttrsScope<HTMLButtonElement>.formNoValidate() =
attr("formnovalidate", "") attr("formnovalidate", "")
fun AttrsBuilder<HTMLButtonElement>.formTarget(value: ButtonFormTarget) = fun AttrsScope<HTMLButtonElement>.formTarget(value: ButtonFormTarget) =
attr("formtarget", value.targetStr) attr("formtarget", value.targetStr)
fun AttrsBuilder<HTMLButtonElement>.name(value: String) = fun AttrsScope<HTMLButtonElement>.name(value: String) =
attr("name", value) attr("name", value)
fun AttrsBuilder<HTMLButtonElement>.type(value: ButtonType) = fun AttrsScope<HTMLButtonElement>.type(value: ButtonType) =
attr("type", value.str) attr("type", value.str)
fun AttrsBuilder<HTMLButtonElement>.value(value: String) = fun AttrsScope<HTMLButtonElement>.value(value: String) =
attr("value", value) attr("value", value)
/* Form attributes */ /* Form attributes */
fun AttrsBuilder<HTMLFormElement>.action(value: String) = fun AttrsScope<HTMLFormElement>.action(value: String) =
attr("action", value) attr("action", value)
fun AttrsBuilder<HTMLFormElement>.acceptCharset(value: String) = fun AttrsScope<HTMLFormElement>.acceptCharset(value: String) =
attr("accept-charset", value) attr("accept-charset", value)
fun AttrsBuilder<HTMLFormElement>.autoComplete(value: Boolean = true) = fun AttrsScope<HTMLFormElement>.autoComplete(value: Boolean = true) =
attr("autocomplete", if(value) "on" else "off") attr("autocomplete", if(value) "on" else "off")
fun AttrsBuilder<HTMLFormElement>.encType(value: FormEncType) = fun AttrsScope<HTMLFormElement>.encType(value: FormEncType) =
attr("enctype", value.typeStr) attr("enctype", value.typeStr)
fun AttrsBuilder<HTMLFormElement>.method(value: FormMethod) = fun AttrsScope<HTMLFormElement>.method(value: FormMethod) =
attr("method", value.methodStr) attr("method", value.methodStr)
fun AttrsBuilder<HTMLFormElement>.noValidate() = fun AttrsScope<HTMLFormElement>.noValidate() =
attr("novalidate", "") attr("novalidate", "")
fun AttrsBuilder<HTMLFormElement>.target(value: FormTarget) = fun AttrsScope<HTMLFormElement>.target(value: FormTarget) =
attr("target", value.targetStr) attr("target", value.targetStr)
fun AttrsBuilder<HTMLFormElement>.onSubmit( fun AttrsScope<HTMLFormElement>.onSubmit(
listener: (SyntheticSubmitEvent) -> Unit listener: (SyntheticSubmitEvent) -> Unit
) { ) {
addEventListener(eventName = EventsListenerBuilder.SUBMIT, listener = listener) addEventListener(eventName = EventsListenerScope.SUBMIT, listener = listener)
} }
fun AttrsBuilder<HTMLFormElement>.onReset( fun AttrsScope<HTMLFormElement>.onReset(
listener: (SyntheticSubmitEvent) -> Unit listener: (SyntheticSubmitEvent) -> Unit
) { ) {
addEventListener(eventName = EventsListenerBuilder.RESET, listener = listener) addEventListener(eventName = EventsListenerScope.RESET, listener = listener)
} }
/* Input attributes */ /* Input attributes */
fun AttrsBuilder<HTMLInputElement>.type(value: InputType<*>) = fun AttrsScope<HTMLInputElement>.type(value: InputType<*>) =
attr("type", value.typeStr) attr("type", value.typeStr)
fun AttrsBuilder<HTMLInputElement>.accept(value: String) = fun AttrsScope<HTMLInputElement>.accept(value: String) =
attr("accept", value) // type: file only attr("accept", value) // type: file only
fun AttrsBuilder<HTMLInputElement>.alt(value: String) = fun AttrsScope<HTMLInputElement>.alt(value: String) =
attr("alt", value) // type: image only attr("alt", value) // type: image only
fun AttrsBuilder<HTMLInputElement>.autoComplete(value: AutoComplete) = fun AttrsScope<HTMLInputElement>.autoComplete(value: AutoComplete) =
attr("autocomplete", value.unsafeCast<String>()) attr("autocomplete", value.unsafeCast<String>())
fun AttrsBuilder<HTMLInputElement>.autoFocus() = fun AttrsScope<HTMLInputElement>.autoFocus() =
attr("autofocus", "") attr("autofocus", "")
fun AttrsBuilder<HTMLInputElement>.capture(value: String) = fun AttrsScope<HTMLInputElement>.capture(value: String) =
attr("capture", value) // type: file only attr("capture", value) // type: file only
fun AttrsBuilder<HTMLInputElement>.dirName(value: String) = fun AttrsScope<HTMLInputElement>.dirName(value: String) =
attr("dirname", value) // text, search attr("dirname", value) // text, search
fun AttrsBuilder<HTMLInputElement>.disabled() = fun AttrsScope<HTMLInputElement>.disabled() =
attr("disabled", "") attr("disabled", "")
fun AttrsBuilder<HTMLInputElement>.form(id: String) = fun AttrsScope<HTMLInputElement>.form(id: String) =
attr("form", id) attr("form", id)
fun AttrsBuilder<HTMLInputElement>.formAction(url: String) = fun AttrsScope<HTMLInputElement>.formAction(url: String) =
attr("formaction", url) attr("formaction", url)
fun AttrsBuilder<HTMLInputElement>.formEncType(value: InputFormEncType) = fun AttrsScope<HTMLInputElement>.formEncType(value: InputFormEncType) =
attr("formenctype", value.typeStr) attr("formenctype", value.typeStr)
fun AttrsBuilder<HTMLInputElement>.formMethod(value: InputFormMethod) = fun AttrsScope<HTMLInputElement>.formMethod(value: InputFormMethod) =
attr("formmethod", value.methodStr) attr("formmethod", value.methodStr)
fun AttrsBuilder<HTMLInputElement>.formNoValidate() = fun AttrsScope<HTMLInputElement>.formNoValidate() =
attr("formnovalidate", "") attr("formnovalidate", "")
fun AttrsBuilder<HTMLInputElement>.formTarget(value: InputFormTarget) = fun AttrsScope<HTMLInputElement>.formTarget(value: InputFormTarget) =
attr("formtarget", value.targetStr) attr("formtarget", value.targetStr)
fun AttrsBuilder<HTMLInputElement>.height(value: Int) = fun AttrsScope<HTMLInputElement>.height(value: Int) =
attr("height", value.toString()) // image only attr("height", value.toString()) // image only
fun AttrsBuilder<HTMLInputElement>.width(value: Int) = fun AttrsScope<HTMLInputElement>.width(value: Int) =
attr("width", value.toString()) // image only attr("width", value.toString()) // image only
fun AttrsBuilder<HTMLInputElement>.list(dataListId: String) = fun AttrsScope<HTMLInputElement>.list(dataListId: String) =
attr("list", dataListId) attr("list", dataListId)
fun AttrsBuilder<HTMLInputElement>.max(value: String) = fun AttrsScope<HTMLInputElement>.max(value: String) =
attr("max", value) attr("max", value)
fun AttrsBuilder<HTMLInputElement>.maxLength(value: Int) = fun AttrsScope<HTMLInputElement>.maxLength(value: Int) =
attr("maxlength", value.toString()) attr("maxlength", value.toString())
fun AttrsBuilder<HTMLInputElement>.min(value: String) = fun AttrsScope<HTMLInputElement>.min(value: String) =
attr("min", value) attr("min", value)
fun AttrsBuilder<HTMLInputElement>.minLength(value: Int) = fun AttrsScope<HTMLInputElement>.minLength(value: Int) =
attr("minlength", value.toString()) attr("minlength", value.toString())
fun AttrsBuilder<HTMLInputElement>.multiple() = fun AttrsScope<HTMLInputElement>.multiple() =
attr("multiple", "") attr("multiple", "")
fun AttrsBuilder<HTMLInputElement>.name(value: String) = fun AttrsScope<HTMLInputElement>.name(value: String) =
attr("name", value) attr("name", value)
fun AttrsBuilder<HTMLInputElement>.pattern(value: String) = fun AttrsScope<HTMLInputElement>.pattern(value: String) =
attr("pattern", value) attr("pattern", value)
fun AttrsBuilder<HTMLInputElement>.placeholder(value: String) = fun AttrsScope<HTMLInputElement>.placeholder(value: String) =
attr("placeholder", value) attr("placeholder", value)
fun AttrsBuilder<HTMLInputElement>.readOnly() = fun AttrsScope<HTMLInputElement>.readOnly() =
attr("readonly", "") attr("readonly", "")
fun AttrsBuilder<HTMLInputElement>.required(value: Boolean = true) = fun AttrsScope<HTMLInputElement>.required(value: Boolean = true) =
attr("required", value.toString()) attr("required", value.toString())
fun AttrsBuilder<HTMLInputElement>.size(value: Int) = fun AttrsScope<HTMLInputElement>.size(value: Int) =
attr("size", value.toString()) attr("size", value.toString())
fun AttrsBuilder<HTMLInputElement>.src(value: String) = fun AttrsScope<HTMLInputElement>.src(value: String) =
attr("src", value) // image only attr("src", value) // image only
fun AttrsBuilder<HTMLInputElement>.step(value: Number) = fun AttrsScope<HTMLInputElement>.step(value: Number) =
attr("step", value.toString()) // numeric types only attr("step", value.toString()) // numeric types only
/* Option attributes */ /* Option attributes */
fun AttrsBuilder<HTMLOptionElement>.value(value: String) = fun AttrsScope<HTMLOptionElement>.value(value: String) =
attr("value", value) attr("value", value)
fun AttrsBuilder<HTMLOptionElement>.disabled() = fun AttrsScope<HTMLOptionElement>.disabled() =
attr("disabled", "") attr("disabled", "")
fun AttrsBuilder<HTMLOptionElement>.selected() = fun AttrsScope<HTMLOptionElement>.selected() =
attr("selected", "") attr("selected", "")
fun AttrsBuilder<HTMLOptionElement>.label(value: String) = fun AttrsScope<HTMLOptionElement>.label(value: String) =
attr("label", value) attr("label", value)
/* Select attributes */ /* Select attributes */
fun AttrsBuilder<HTMLSelectElement>.autoComplete(value: AutoComplete) = fun AttrsScope<HTMLSelectElement>.autoComplete(value: AutoComplete) =
attr("autocomplete", value.unsafeCast<String>()) attr("autocomplete", value.unsafeCast<String>())
fun AttrsBuilder<HTMLSelectElement>.autofocus() = fun AttrsScope<HTMLSelectElement>.autofocus() =
attr("autofocus", "") attr("autofocus", "")
fun AttrsBuilder<HTMLSelectElement>.disabled() = fun AttrsScope<HTMLSelectElement>.disabled() =
attr("disabled", "") attr("disabled", "")
fun AttrsBuilder<HTMLSelectElement>.form(formId: String) = fun AttrsScope<HTMLSelectElement>.form(formId: String) =
attr("form", formId) attr("form", formId)
fun AttrsBuilder<HTMLSelectElement>.multiple() = fun AttrsScope<HTMLSelectElement>.multiple() =
attr("multiple", "") attr("multiple", "")
fun AttrsBuilder<HTMLSelectElement>.name(value: String) = fun AttrsScope<HTMLSelectElement>.name(value: String) =
attr("name", value) attr("name", value)
fun AttrsBuilder<HTMLSelectElement>.required() = fun AttrsScope<HTMLSelectElement>.required() =
attr("required", "") attr("required", "")
fun AttrsBuilder<HTMLSelectElement>.size(numberOfRows: Int) = fun AttrsScope<HTMLSelectElement>.size(numberOfRows: Int) =
attr("size", numberOfRows.toString()) attr("size", numberOfRows.toString())
/* OptGroup attributes */ /* OptGroup attributes */
fun AttrsBuilder<HTMLOptGroupElement>.label(value: String) = fun AttrsScope<HTMLOptGroupElement>.label(value: String) =
attr("label", value) attr("label", value)
fun AttrsBuilder<HTMLOptGroupElement>.disabled() = fun AttrsScope<HTMLOptGroupElement>.disabled() =
attr("disabled", "") attr("disabled", "")
/* TextArea attributes */ /* TextArea attributes */
fun AttrsBuilder<HTMLTextAreaElement>.autoComplete(value: AutoComplete) = fun AttrsScope<HTMLTextAreaElement>.autoComplete(value: AutoComplete) =
attr("autocomplete", value.unsafeCast<String>()) attr("autocomplete", value.unsafeCast<String>())
fun AttrsBuilder<HTMLTextAreaElement>.autoFocus() = fun AttrsScope<HTMLTextAreaElement>.autoFocus() =
attr("autofocus", "") attr("autofocus", "")
fun AttrsBuilder<HTMLTextAreaElement>.cols(value: Int) = fun AttrsScope<HTMLTextAreaElement>.cols(value: Int) =
attr("cols", value.toString()) attr("cols", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.disabled() = fun AttrsScope<HTMLTextAreaElement>.disabled() =
attr("disabled", "") attr("disabled", "")
fun AttrsBuilder<HTMLTextAreaElement>.form(formId: String) = fun AttrsScope<HTMLTextAreaElement>.form(formId: String) =
attr("form", formId) attr("form", formId)
fun AttrsBuilder<HTMLTextAreaElement>.maxLength(value: Int) = fun AttrsScope<HTMLTextAreaElement>.maxLength(value: Int) =
attr("maxlength", value.toString()) attr("maxlength", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.minLength(value: Int) = fun AttrsScope<HTMLTextAreaElement>.minLength(value: Int) =
attr("minlength", value.toString()) attr("minlength", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.name(value: String) = fun AttrsScope<HTMLTextAreaElement>.name(value: String) =
attr("name", value) attr("name", value)
fun AttrsBuilder<HTMLTextAreaElement>.placeholder(value: String) = fun AttrsScope<HTMLTextAreaElement>.placeholder(value: String) =
attr("placeholder", value) attr("placeholder", value)
fun AttrsBuilder<HTMLTextAreaElement>.readOnly() = fun AttrsScope<HTMLTextAreaElement>.readOnly() =
attr("readonly", "") attr("readonly", "")
fun AttrsBuilder<HTMLTextAreaElement>.required() = fun AttrsScope<HTMLTextAreaElement>.required() =
attr("required", "") attr("required", "")
fun AttrsBuilder<HTMLTextAreaElement>.rows(value: Int) = fun AttrsScope<HTMLTextAreaElement>.rows(value: Int) =
attr("rows", value.toString()) attr("rows", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.wrap(value: TextAreaWrap) = fun AttrsScope<HTMLTextAreaElement>.wrap(value: TextAreaWrap) =
attr("wrap", value.str) attr("wrap", value.str)
/* Img attributes */ /* Img attributes */
fun AttrsBuilder<HTMLImageElement>.src(value: String): AttrsBuilder<HTMLImageElement> = fun AttrsScope<HTMLImageElement>.src(value: String): AttrsScope<HTMLImageElement> =
attr("src", value) attr("src", value)
fun AttrsBuilder<HTMLImageElement>.alt(value: String): AttrsBuilder<HTMLImageElement> = fun AttrsScope<HTMLImageElement>.alt(value: String): AttrsScope<HTMLImageElement> =
attr("alt", value) attr("alt", value)
@ -313,19 +313,19 @@ internal val setCheckedValue: (HTMLInputElement, Boolean) -> Unit = { e, v ->
} }
/* Img attributes */ /* Img attributes */
fun AttrsBuilder<HTMLLabelElement>.forId(value: String): AttrsBuilder<HTMLLabelElement> = fun AttrsScope<HTMLLabelElement>.forId(value: String): AttrsScope<HTMLLabelElement> =
attr("for", value) attr("for", value)
/* Table attributes */ /* Table attributes */
fun AttrsBuilder<HTMLTableColElement>.span(value: Int): AttrsBuilder<HTMLTableColElement> = fun AttrsScope<HTMLTableColElement>.span(value: Int): AttrsScope<HTMLTableColElement> =
attr("span", value.toString()) attr("span", value.toString())
fun AttrsBuilder<HTMLTableCellElement>.scope(value: Scope): AttrsBuilder<HTMLTableCellElement> = fun AttrsScope<HTMLTableCellElement>.scope(value: Scope): AttrsScope<HTMLTableCellElement> =
attr("scope", value.str) attr("scope", value.str)
fun AttrsBuilder<HTMLTableCellElement>.colspan(value: Int): AttrsBuilder<HTMLTableCellElement> = fun AttrsScope<HTMLTableCellElement>.colspan(value: Int): AttrsScope<HTMLTableCellElement> =
attr("colspan", value.toString()) attr("colspan", value.toString())
fun AttrsBuilder<HTMLTableCellElement>.rowspan(value: Int): AttrsBuilder<HTMLTableCellElement> = fun AttrsScope<HTMLTableCellElement>.rowspan(value: Int): AttrsScope<HTMLTableCellElement> =
attr("rowspan", value.toString()) attr("rowspan", value.toString())

148
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/AttrsBuilder.kt → web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/AttrsScope.kt

@ -2,35 +2,35 @@ package org.jetbrains.compose.web.attributes
import androidx.compose.runtime.DisposableEffectResult import androidx.compose.runtime.DisposableEffectResult
import androidx.compose.runtime.DisposableEffectScope import androidx.compose.runtime.DisposableEffectScope
import org.jetbrains.compose.web.css.StyleBuilder import org.jetbrains.compose.web.css.StyleScope
import org.jetbrains.compose.web.css.StyleBuilderImpl import org.jetbrains.compose.web.css.StyleScopeBuilder
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
/** /**
* [AttrsBuilder] is a class that is used (as a builder context, that is as AttrsBuilder<T>.() -> Unit) * [AttrsScope] is a class that is used (as a builder context, that is as AttrsBuilder<T>.() -> Unit)
* in all DOM-element creating API calls. It's used for adding attributes to the element created, * in all DOM-element creating API calls. It's used for adding attributes to the element created,
* adding inline style values (via [style]) and attaching events to the element (since AttrsBuilder * adding inline style values (via [style]) and attaching events to the element (since AttrsBuilder
* is an [EventsListenerBuilder]) * is an [EventsListenerScope])
* *
* In that aspect the most important method is [attr]. Setting the most frequently attributes, like [id], [tabIndex] * In that aspect the most important method is [attr]. Setting the most frequently attributes, like [id], [tabIndex]
* are extracted to a separate methods. * are extracted to a separate methods.
* *
*/ */
open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() { interface AttrsScope<TElement : Element>: EventsListenerScope {
internal val attributesMap = mutableMapOf<String, String>() @ComposeWebInternalApi
internal val styleBuilder = StyleBuilderImpl() val attributesMap: Map<String, String>
@ComposeWebInternalApi
internal val propertyUpdates = mutableListOf<Pair<(Element, Any) -> Unit, Any>>() val styleScope: StyleScope
internal var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)? = null
@ComposeWebInternalApi
internal var inputControlledValueSet = false val propertyUpdates: List<Pair<(Element, Any) -> Unit, Any>>
internal var inputDefaultValueSet = false @ComposeWebInternalApi
internal var inputControlledCheckedSet = false var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)?
internal var inputDefaultCheckedSet = false
/** /**
* [style] add inline CSS-style properties to the element via [StyleBuilder] context * [style] add inline CSS-style properties to the element via [StyleScope] context
* *
* Example: * Example:
* ``` * ```
@ -39,18 +39,18 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
* }) * })
* ``` * ```
*/ */
fun style(builder: StyleBuilder.() -> Unit) { fun style(builder: StyleScope.() -> Unit) {
styleBuilder.apply(builder) styleScope.apply(builder)
} }
/** /**
* [classes] adds all values passed as params to the element's classList. * [classes] adds all values passed as params to the element's classList.
* This method acts cumulatively, that is, each call adds values to the classList. * This method acts cumulatively, that is, each call adds values to the classList.
* In the ideology of Composable functions and their recomposition one just don't need to remove classes, * In the ideology of Composable functions and their recomposition one just don't need to remove classes,
* since if your classList is, for instance, condition-dependent, you can always just call this method conditionally. * since if your classList is, for instance, condition-dependent, you can always just call this method conditionally.
*/ */
fun classes(vararg classes: String) = prop(setClassList, classes) fun classes(vararg classes: String)= prop(setClassList, classes)
fun id(value: String) = attr(ID, value) fun id(value: String) = attr(ID, value)
fun hidden() = attr(HIDDEN, true.toString()) fun hidden() = attr(HIDDEN, true.toString())
fun title(value: String) = attr(TITLE, value) fun title(value: String) = attr(TITLE, value)
@ -60,17 +60,80 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
fun lang(value: String) = attr(LANG, value) fun lang(value: String) = attr(LANG, value)
fun tabIndex(value: Int) = attr(TAB_INDEX, value.toString()) fun tabIndex(value: Int) = attr(TAB_INDEX, value.toString())
fun spellCheck(value: Boolean) = attr(SPELLCHECK, value.toString()) fun spellCheck(value: Boolean) = attr(SPELLCHECK, value.toString())
/** /**
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode * see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode
*/ */
fun inputMode(value: String) = attr("inputmode", value) fun inputMode(value: String) = attr("inputmode", value)
/** /**
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode * see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode
*/ */
fun inputMode(value: InputMode) = attr("inputmode", value.str) fun inputMode(value: InputMode) = attr("inputmode", value.str)
/**
* [ref] can be used to retrieve a reference to a html element.
* The lambda that `ref` takes in is not Composable. It will be called only once when an element added into a composition.
* Likewise, the lambda passed in `onDispose` will be called only once when an element leaves the composition.
*
* Under the hood, `ref` uses [DisposableEffect](https://developer.android.com/jetpack/compose/side-effects#disposableeffect)
*/
fun ref(effect: DisposableEffectScope.(TElement) -> DisposableEffectResult)
/**
* [attr] adds arbitrary attribute to the Element.
* If it called twice for the same attribute name, attribute value will be resolved to the last call.
*
* @param attr - the name of the attribute
* @param value - the value of the attribute
*
* For boolean attributes cast boolean value to String and pass it as value.
*/
fun attr(attr: String, value: String): AttrsScope<TElement>
/**
* [prop] allows setting values of element's properties which can't be set by ussing [attr].
* [update] is a lambda with two parameters: `element` and `value`. `element` is a reference to a native element.
* Some examples of properties that can set using [prop]: `value`, `checked`, `innerText`.
*
* Unlike [ref], lambda passed to [prop] will be invoked every time when AttrsBuilder being called during recomposition.
* [prop] is not supposed to be used for adding listeners, subscriptions, etc.
* Also see [ref].
*
* Code Example:
* ```
* Input(type = InputType.Text, attrs = {
* // This is only an example. One doesn't need to set `value` like this, since [Input] has `value(v: String)`
* prop({ element: HTMLInputElement, value: String -> element.value = value }, "someTextInputValue")
* })
* ```
*/
@Suppress("UNCHECKED_CAST")
fun <E : HTMLElement, V> prop(update: (E, V) -> Unit, value: V)
fun copyFrom(attrsScope: AttrsScope<TElement>)
companion object {
const val CLASS = "class"
const val ID = "id"
const val HIDDEN = "hidden"
const val TITLE = "title"
const val DIR = "dir"
const val DRAGGABLE = "draggable"
const val CONTENT_EDITABLE = "contenteditable"
const val LANG = "lang"
const val TAB_INDEX = "tabindex"
const val SPELLCHECK = "spellcheck"
}
}
open class AttrsScopeBuilder<TElement : Element> : AttrsScope<TElement>, EventsListenerScope by EventsListenerScopeBuilder() {
override val attributesMap = mutableMapOf<String, String>()
override val styleScope: StyleScope = StyleScopeBuilder()
override val propertyUpdates = mutableListOf<Pair<(Element, Any) -> Unit, Any>>()
override var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)? = null
/** /**
* [ref] can be used to retrieve a reference to a html element. * [ref] can be used to retrieve a reference to a html element.
* The lambda that `ref` takes in is not Composable. It will be called only once when an element added into a composition. * The lambda that `ref` takes in is not Composable. It will be called only once when an element added into a composition.
@ -78,7 +141,7 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
* *
* Under the hood, `ref` uses [DisposableEffect](https://developer.android.com/jetpack/compose/side-effects#disposableeffect) * Under the hood, `ref` uses [DisposableEffect](https://developer.android.com/jetpack/compose/side-effects#disposableeffect)
*/ */
fun ref(effect: DisposableEffectScope.(TElement) -> DisposableEffectResult) { override fun ref(effect: DisposableEffectScope.(TElement) -> DisposableEffectResult) {
this.refEffect = effect this.refEffect = effect
} }
@ -91,7 +154,7 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
* *
* For boolean attributes cast boolean value to String and pass it as value. * For boolean attributes cast boolean value to String and pass it as value.
*/ */
fun attr(attr: String, value: String): AttrsBuilder<TElement> { override fun attr(attr: String, value: String): AttrsScope<TElement> {
attributesMap[attr] = value attributesMap[attr] = value
return this return this
} }
@ -114,35 +177,22 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
* ``` * ```
*/ */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <E : HTMLElement, V> prop(update: (E, V) -> Unit, value: V) { override fun <E : HTMLElement, V> prop(update: (E, V) -> Unit, value: V) {
propertyUpdates.add((update to value) as Pair<(Element, Any) -> Unit, Any>) propertyUpdates.add((update to value) as Pair<(Element, Any) -> Unit, Any>)
} }
internal fun collect(): Map<String, String> { internal fun collect(): Map<String, String> {
return attributesMap return attributesMap
} }
internal fun copyFrom(attrsBuilder: AttrsBuilder<TElement>) { override fun copyFrom(attrsScope: AttrsScope<TElement>) {
refEffect = attrsBuilder.refEffect refEffect = attrsScope.refEffect
styleBuilder.copyFrom(attrsBuilder.styleBuilder) styleScope.copyFrom(attrsScope.styleScope)
attributesMap.putAll(attrsBuilder.attributesMap) attributesMap.putAll(attrsScope.attributesMap)
propertyUpdates.addAll(attrsBuilder.propertyUpdates) propertyUpdates.addAll(attrsScope.propertyUpdates)
copyListenersFrom(attrsBuilder) copyListenersFrom(attrsScope)
}
companion object {
const val CLASS = "class"
const val ID = "id"
const val HIDDEN = "hidden"
const val TITLE = "title"
const val DIR = "dir"
const val DRAGGABLE = "draggable"
const val CONTENT_EDITABLE = "contenteditable"
const val LANG = "lang"
const val TAB_INDEX = "tabindex"
const val SPELLCHECK = "spellcheck"
} }
} }

232
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/EventsListenerBuilder.kt → web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/EventsListenerScope.kt

@ -5,191 +5,200 @@ import androidx.compose.web.events.SyntheticEvent
import androidx.compose.web.events.SyntheticMouseEvent import androidx.compose.web.events.SyntheticMouseEvent
import androidx.compose.web.events.SyntheticWheelEvent import androidx.compose.web.events.SyntheticWheelEvent
import org.jetbrains.compose.web.events.* import org.jetbrains.compose.web.events.*
import org.w3c.dom.HTMLFormElement import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.w3c.dom.events.EventTarget import org.w3c.dom.events.EventTarget
private typealias SyntheticMouseEventListener = (SyntheticMouseEvent) -> Unit private typealias SyntheticMouseEventListener = (SyntheticMouseEvent) -> Unit
private typealias SyntheticWheelEventListener = (SyntheticWheelEvent) -> Unit private typealias SyntheticWheelEventListener = (SyntheticWheelEvent) -> Unit
private typealias SyntheticDragEventListener = (SyntheticDragEvent) -> Unit private typealias SyntheticDragEventListener = (SyntheticDragEvent) -> Unit
@Deprecated(
message = "Renamed to EventsListenerScopeBuilder",
replaceWith = ReplaceWith("EventsListenerScopeBuilder", "org.jetbrains.compose.web.attributes.EventsListenerScopeBuilder")
)
typealias EventsListenerBuilder = EventsListenerScopeBuilder
/** /**
* [EventsListenerBuilder] is used most often not directly but via [AttrsBuilder]. * [EventsListenerScope] is used most often not directly but via [AttrsScope].
* Its purpose is to add events to the element. For all most frequently used events there * Its purpose is to add events to the element. For all most frequently used events there
* exist dedicated method. In case you need to support event that doesn't have such method, * exist dedicated method. In case you need to support event that doesn't have such method,
* use [addEventListener] * use [addEventListener]
*/ */
open class EventsListenerBuilder { interface EventsListenerScope {
@ComposeWebInternalApi
protected val listeners = mutableListOf<SyntheticEventListener<*>>() val listeners: List<SyntheticEventListener<*>>
@ComposeWebInternalApi
fun registerEventListener(listener: SyntheticEventListener<*>)
/* Mouse Events */ /* Mouse Events */
fun onContextMenu(listener: SyntheticMouseEventListener) { fun onContextMenu(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(CONTEXTMENU, listener)) registerEventListener(MouseEventListener(CONTEXTMENU, listener))
} }
fun onClick(listener: SyntheticMouseEventListener) { fun onClick(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(CLICK, listener)) registerEventListener(MouseEventListener(CLICK, listener))
} }
fun onDoubleClick(listener: SyntheticMouseEventListener) { fun onDoubleClick(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(DBLCLICK, listener)) registerEventListener(MouseEventListener(DBLCLICK, listener))
} }
fun onMouseDown(listener: SyntheticMouseEventListener) { fun onMouseDown(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEDOWN, listener)) registerEventListener(MouseEventListener(MOUSEDOWN, listener))
} }
fun onMouseUp(listener: SyntheticMouseEventListener) { fun onMouseUp(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEUP, listener)) registerEventListener(MouseEventListener(MOUSEUP, listener))
} }
fun onMouseEnter(listener: SyntheticMouseEventListener) { fun onMouseEnter(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEENTER, listener)) registerEventListener(MouseEventListener(MOUSEENTER, listener))
} }
fun onMouseLeave(listener: SyntheticMouseEventListener) { fun onMouseLeave(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSELEAVE, listener)) registerEventListener(MouseEventListener(MOUSELEAVE, listener))
} }
fun onMouseMove(listener: SyntheticMouseEventListener) { fun onMouseMove(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEMOVE, listener)) registerEventListener(MouseEventListener(MOUSEMOVE, listener))
} }
fun onMouseOut(listener: SyntheticMouseEventListener) { fun onMouseOut(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEOUT, listener)) registerEventListener(MouseEventListener(MOUSEOUT, listener))
} }
fun onMouseOver(listener: SyntheticMouseEventListener) { fun onMouseOver(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEOVER, listener)) registerEventListener(MouseEventListener(MOUSEOVER, listener))
} }
fun onWheel(listener: SyntheticWheelEventListener) { fun onWheel(listener: SyntheticWheelEventListener) {
listeners.add(MouseWheelEventListener(WHEEL, listener)) registerEventListener(MouseWheelEventListener(WHEEL, listener))
} }
/* Drag Events */ /* Drag Events */
fun onDrag(listener: SyntheticDragEventListener) { fun onDrag(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAG, listener)) registerEventListener(DragEventListener(DRAG, listener))
} }
fun onDrop(listener: SyntheticDragEventListener) { fun onDrop(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DROP, listener)) registerEventListener(DragEventListener(DROP, listener))
} }
fun onDragStart(listener: SyntheticDragEventListener) { fun onDragStart(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGSTART, listener)) registerEventListener(DragEventListener(DRAGSTART, listener))
} }
fun onDragEnd(listener: SyntheticDragEventListener) { fun onDragEnd(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGEND, listener)) registerEventListener(DragEventListener(DRAGEND, listener))
} }
fun onDragOver(listener: SyntheticDragEventListener) { fun onDragOver(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGOVER, listener)) registerEventListener(DragEventListener(DRAGOVER, listener))
} }
fun onDragEnter(listener: SyntheticDragEventListener) { fun onDragEnter(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGENTER, listener)) registerEventListener(DragEventListener(DRAGENTER, listener))
} }
fun onDragLeave(listener: SyntheticDragEventListener) { fun onDragLeave(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGLEAVE, listener)) registerEventListener(DragEventListener(DRAGLEAVE, listener))
} }
/* End of Drag Events */ /* End of Drag Events */
/* Clipboard Events */ /* Clipboard Events */
fun onCopy(listener: (SyntheticClipboardEvent) -> Unit) { fun onCopy(listener: (SyntheticClipboardEvent) -> Unit) {
listeners.add(ClipboardEventListener(COPY, listener)) registerEventListener(ClipboardEventListener(COPY, listener))
} }
fun onCut(listener: (SyntheticClipboardEvent) -> Unit) { fun onCut(listener: (SyntheticClipboardEvent) -> Unit) {
listeners.add(ClipboardEventListener(CUT, listener)) registerEventListener(ClipboardEventListener(CUT, listener))
} }
fun onPaste(listener: (SyntheticClipboardEvent) -> Unit) { fun onPaste(listener: (SyntheticClipboardEvent) -> Unit) {
listeners.add(ClipboardEventListener(PASTE, listener)) registerEventListener(ClipboardEventListener(PASTE, listener))
} }
/* End of Clipboard Events */ /* End of Clipboard Events */
/* Keyboard Events */ /* Keyboard Events */
fun onKeyDown(listener: (SyntheticKeyboardEvent) -> Unit) { fun onKeyDown(listener: (SyntheticKeyboardEvent) -> Unit) {
listeners.add(KeyboardEventListener(KEYDOWN, listener)) registerEventListener(KeyboardEventListener(KEYDOWN, listener))
} }
fun onKeyUp(listener: (SyntheticKeyboardEvent) -> Unit) { fun onKeyUp(listener: (SyntheticKeyboardEvent) -> Unit) {
listeners.add(KeyboardEventListener(KEYUP, listener)) registerEventListener(KeyboardEventListener(KEYUP, listener))
} }
/* End of Keyboard Events */ /* End of Keyboard Events */
/* Focus Events */ /* Focus Events */
fun onFocus(listener: (SyntheticFocusEvent) -> Unit) { fun onFocus(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(FOCUS, listener)) registerEventListener(FocusEventListener(FOCUS, listener))
} }
fun onBlur(listener: (SyntheticFocusEvent) -> Unit) { fun onBlur(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(BLUR, listener)) registerEventListener(FocusEventListener(BLUR, listener))
} }
fun onFocusIn(listener: (SyntheticFocusEvent) -> Unit) { fun onFocusIn(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(FOCUSIN, listener)) registerEventListener(FocusEventListener(FOCUSIN, listener))
} }
fun onFocusOut(listener: (SyntheticFocusEvent) -> Unit) { fun onFocusOut(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(FOCUSOUT, listener)) registerEventListener(FocusEventListener(FOCUSOUT, listener))
} }
/* End of Focus Events */ /* End of Focus Events */
/* Touch Events */ /* Touch Events */
fun onTouchCancel(listener: (SyntheticTouchEvent) -> Unit) { fun onTouchCancel(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHCANCEL, listener)) registerEventListener(TouchEventListener(TOUCHCANCEL, listener))
} }
fun onTouchMove(listener: (SyntheticTouchEvent) -> Unit) { fun onTouchMove(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHMOVE, listener)) registerEventListener(TouchEventListener(TOUCHMOVE, listener))
} }
fun onTouchEnd(listener: (SyntheticTouchEvent) -> Unit) { fun onTouchEnd(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHEND, listener)) registerEventListener(TouchEventListener(TOUCHEND, listener))
} }
fun onTouchStart(listener: (SyntheticTouchEvent) -> Unit) { fun onTouchStart(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHSTART, listener)) registerEventListener(TouchEventListener(TOUCHSTART, listener))
} }
/* End of Touch Events */ /* End of Touch Events */
/* Animation Events */ /* Animation Events */
fun onAnimationEnd(listener: (SyntheticAnimationEvent) -> Unit) { fun onAnimationEnd(listener: (SyntheticAnimationEvent) -> Unit) {
listeners.add(AnimationEventListener(ANIMATIONEND, listener)) registerEventListener(AnimationEventListener(ANIMATIONEND, listener))
} }
fun onAnimationIteration(listener: (SyntheticAnimationEvent) -> Unit) { fun onAnimationIteration(listener: (SyntheticAnimationEvent) -> Unit) {
listeners.add(AnimationEventListener(ANIMATIONITERATION, listener)) registerEventListener(AnimationEventListener(ANIMATIONITERATION, listener))
} }
fun onAnimationStart(listener: (SyntheticAnimationEvent) -> Unit) { fun onAnimationStart(listener: (SyntheticAnimationEvent) -> Unit) {
listeners.add(AnimationEventListener(ANIMATIONSTART, listener)) registerEventListener(AnimationEventListener(ANIMATIONSTART, listener))
} }
/* End of Animation Events */ /* End of Animation Events */
fun onScroll(listener: (SyntheticEvent<EventTarget>) -> Unit) { fun onScroll(listener: (SyntheticEvent<EventTarget>) -> Unit) {
listeners.add(SyntheticEventListener(SCROLL, listener)) registerEventListener(SyntheticEventListener(SCROLL, listener))
} }
internal fun collectListeners(): List<SyntheticEventListener<*>> = listeners fun collectListeners(): List<SyntheticEventListener<*>> = listeners
/** /**
* [addEventListener] used for adding arbitrary events to the element. It resembles the standard DOM addEventListener method * [addEventListener] used for adding arbitrary events to the element. It resembles the standard DOM addEventListener method
* @param eventName - the name of the event * @param eventName - the name of the event
@ -200,33 +209,32 @@ open class EventsListenerBuilder {
eventName: String, eventName: String,
listener: (T) -> Unit listener: (T) -> Unit
) { ) {
listeners.add(SyntheticEventListener(eventName, listener)) registerEventListener(SyntheticEventListener(eventName, listener))
} }
fun addEventListener( fun addEventListener(
eventName: String, eventName: String,
listener: (SyntheticEvent<EventTarget>) -> Unit listener: (SyntheticEvent<EventTarget>) -> Unit
) { ) {
listeners.add(SyntheticEventListener(eventName, listener)) registerEventListener(SyntheticEventListener(eventName, listener))
} }
internal fun copyListenersFrom(from: EventsListenerBuilder) { @ComposeWebInternalApi
listeners.addAll(from.listeners) fun copyListenersFrom(from: EventsListenerScope)
}
companion object { companion object {
const val COPY = "copy" const val COPY = "copy"
const val CUT = "cut" const val CUT = "cut"
const val PASTE = "paste" const val PASTE = "paste"
const val CONTEXTMENU = "contextmenu" const val CONTEXTMENU = "contextmenu"
const val CLICK = "click" const val CLICK = "click"
const val DBLCLICK = "dblclick" const val DBLCLICK = "dblclick"
const val FOCUS = "focus" const val FOCUS = "focus"
const val BLUR = "blur" const val BLUR = "blur"
const val FOCUSIN = "focusin" const val FOCUSIN = "focusin"
const val FOCUSOUT = "focusout" const val FOCUSOUT = "focusout"
const val KEYDOWN = "keydown" const val KEYDOWN = "keydown"
const val KEYUP = "keyup" const val KEYUP = "keyup"
const val MOUSEDOWN = "mousedown" const val MOUSEDOWN = "mousedown"
@ -239,22 +247,22 @@ open class EventsListenerBuilder {
const val WHEEL = "wheel" const val WHEEL = "wheel"
const val SCROLL = "scroll" const val SCROLL = "scroll"
const val SELECT = "select" const val SELECT = "select"
const val TOUCHCANCEL = "touchcancel" const val TOUCHCANCEL = "touchcancel"
const val TOUCHEND = "touchend" const val TOUCHEND = "touchend"
const val TOUCHMOVE = "touchmove" const val TOUCHMOVE = "touchmove"
const val TOUCHSTART = "touchstart" const val TOUCHSTART = "touchstart"
const val ANIMATIONCANCEL = "animationcancel" // firefox and safari only const val ANIMATIONCANCEL = "animationcancel" // firefox and safari only
const val ANIMATIONEND = "animationend" const val ANIMATIONEND = "animationend"
const val ANIMATIONITERATION = "animationiteration" const val ANIMATIONITERATION = "animationiteration"
const val ANIMATIONSTART = "animationstart" const val ANIMATIONSTART = "animationstart"
const val BEFOREINPUT = "beforeinput" const val BEFOREINPUT = "beforeinput"
const val INPUT = "input" const val INPUT = "input"
const val CHANGE = "change" const val CHANGE = "change"
const val INVALID = "invalid" const val INVALID = "invalid"
const val DRAG = "drag" const val DRAG = "drag"
const val DROP = "drop" const val DROP = "drop"
const val DRAGSTART = "dragstart" const val DRAGSTART = "dragstart"
@ -262,8 +270,20 @@ open class EventsListenerBuilder {
const val DRAGOVER = "dragover" const val DRAGOVER = "dragover"
const val DRAGENTER = "dragenter" const val DRAGENTER = "dragenter"
const val DRAGLEAVE = "dragleave" const val DRAGLEAVE = "dragleave"
const val SUBMIT = "submit" const val SUBMIT = "submit"
const val RESET = "reset" const val RESET = "reset"
} }
} }
open class EventsListenerScopeBuilder: EventsListenerScope {
override val listeners: MutableList<SyntheticEventListener<*>> = mutableListOf()
override fun registerEventListener(listener: SyntheticEventListener<*>) {
listeners.add(listener)
}
override fun copyListenersFrom(from: EventsListenerScope) {
listeners.addAll(from.listeners)
}
}

6
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/SyntheticEventListener.kt

@ -5,9 +5,9 @@ import androidx.compose.web.events.SyntheticDragEvent
import androidx.compose.web.events.SyntheticEvent import androidx.compose.web.events.SyntheticEvent
import androidx.compose.web.events.SyntheticMouseEvent import androidx.compose.web.events.SyntheticMouseEvent
import androidx.compose.web.events.SyntheticWheelEvent import androidx.compose.web.events.SyntheticWheelEvent
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.CHANGE import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.CHANGE
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.INPUT import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.INPUT
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.SELECT import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.SELECT
import org.jetbrains.compose.web.events.* import org.jetbrains.compose.web.events.*
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.jetbrains.compose.web.internal.runtime.NamedEventListener import org.jetbrains.compose.web.internal.runtime.NamedEventListener

37
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/InputAttrsBuilder.kt → web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/InputAttrsScope.kt

@ -7,13 +7,22 @@ package org.jetbrains.compose.web.attributes.builders
import androidx.compose.web.events.SyntheticEvent import androidx.compose.web.events.SyntheticEvent
import org.jetbrains.compose.web.attributes.* import org.jetbrains.compose.web.attributes.*
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.BEFOREINPUT
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.INPUT
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.INVALID
import org.jetbrains.compose.web.events.SyntheticChangeEvent import org.jetbrains.compose.web.events.SyntheticChangeEvent
import org.jetbrains.compose.web.events.SyntheticInputEvent import org.jetbrains.compose.web.events.SyntheticInputEvent
import org.jetbrains.compose.web.events.SyntheticSelectEvent import org.jetbrains.compose.web.events.SyntheticSelectEvent
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
@Deprecated(
message = "Renamed to InputAttrsScope<T>",
replaceWith = ReplaceWith("InputAttrsScope", "org.jetbrains.compose.web.attributes.builders.InputAttrsScope")
)
typealias InputAttrsBuilder<T> = InputAttrsScope<T>
/** /**
* An extension of [AttrsBuilder]. * An extension of [AttrsScope].
* This class provides a set of methods specific for [Input] element: * This class provides a set of methods specific for [Input] element:
* *
* [value] - sets the current input's value. * [value] - sets the current input's value.
@ -28,11 +37,11 @@ import org.w3c.dom.HTMLInputElement
* [onBeforeInput] - add `beforeinput` event listener * [onBeforeInput] - add `beforeinput` event listener
* [onSelect] - add `select` event listener * [onSelect] - add `select` event listener
*/ */
class InputAttrsBuilder<ValueType>( class InputAttrsScope<ValueType>(
val inputType: InputType<ValueType> val inputType: InputType<ValueType>
) : AttrsBuilder<HTMLInputElement>() { ) : AttrsScopeBuilder<HTMLInputElement>() {
fun value(value: String): InputAttrsBuilder<ValueType> { fun value(value: String): InputAttrsScope<ValueType> {
when (inputType) { when (inputType) {
InputType.Checkbox, InputType.Checkbox,
InputType.Radio, InputType.Radio,
@ -43,27 +52,27 @@ class InputAttrsBuilder<ValueType>(
return this return this
} }
fun value(value: Number): InputAttrsBuilder<ValueType> { fun value(value: Number): InputAttrsScope<ValueType> {
value(value.toString()) value(value.toString())
return this return this
} }
fun checked(checked: Boolean): InputAttrsBuilder<ValueType> { fun checked(checked: Boolean): InputAttrsScope<ValueType> {
prop(setCheckedValue, checked) prop(setCheckedValue, checked)
return this return this
} }
fun defaultChecked(): InputAttrsBuilder<ValueType> { fun defaultChecked(): InputAttrsScope<ValueType> {
attr("checked", "") attr("checked", "")
return this return this
} }
fun defaultValue(value: String): InputAttrsBuilder<ValueType> { fun defaultValue(value: String): InputAttrsScope<ValueType> {
attr("value", value) attr("value", value)
return this return this
} }
fun defaultValue(value: Number): InputAttrsBuilder<ValueType> { fun defaultValue(value: Number): InputAttrsScope<ValueType> {
attr("value", value.toString()) attr("value", value.toString())
return this return this
} }
@ -77,25 +86,25 @@ class InputAttrsBuilder<ValueType>(
fun onInput( fun onInput(
listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit
) { ) {
listeners.add(InputEventListener(eventName = INPUT, inputType, listener)) registerEventListener(InputEventListener(eventName = INPUT, inputType, listener))
} }
fun onChange( fun onChange(
listener: (SyntheticChangeEvent<ValueType, HTMLInputElement>) -> Unit listener: (SyntheticChangeEvent<ValueType, HTMLInputElement>) -> Unit
) { ) {
listeners.add(ChangeEventListener(inputType, listener)) registerEventListener(ChangeEventListener(inputType, listener))
} }
fun onBeforeInput( fun onBeforeInput(
listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit
) { ) {
listeners.add(InputEventListener(eventName = BEFOREINPUT, inputType, listener)) registerEventListener(InputEventListener(eventName = BEFOREINPUT, inputType, listener))
} }
fun onSelect( fun onSelect(
listener: (SyntheticSelectEvent<HTMLInputElement>) -> Unit listener: (SyntheticSelectEvent<HTMLInputElement>) -> Unit
) { ) {
listeners.add(SelectEventListener(listener)) registerEventListener(SelectEventListener(listener))
} }
} }
@ -105,5 +114,3 @@ internal external interface JsWeakMap {
fun has(key: Any): Boolean fun has(key: Any): Boolean
fun set(key: Any, value: Any): JsWeakMap fun set(key: Any, value: Any): JsWeakMap
} }

18
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/SelectAttrsBuilder.kt → web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/SelectAttrsScope.kt

@ -5,27 +5,33 @@
package androidx.compose.web.attributes package androidx.compose.web.attributes
import org.jetbrains.compose.web.attributes.AttrsBuilder import org.jetbrains.compose.web.attributes.AttrsScopeBuilder
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.CHANGE import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.CHANGE
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.INPUT import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.INPUT
import org.jetbrains.compose.web.attributes.SyntheticEventListener import org.jetbrains.compose.web.attributes.SyntheticEventListener
import org.jetbrains.compose.web.events.SyntheticChangeEvent import org.jetbrains.compose.web.events.SyntheticChangeEvent
import org.jetbrains.compose.web.events.SyntheticInputEvent import org.jetbrains.compose.web.events.SyntheticInputEvent
import org.w3c.dom.HTMLSelectElement import org.w3c.dom.HTMLSelectElement
import org.w3c.dom.events.Event import org.w3c.dom.events.Event
class SelectAttrsBuilder : AttrsBuilder<HTMLSelectElement>() { @Deprecated(
message = "Renamed to SelectAttrsScope",
replaceWith = ReplaceWith("SelectAttrsScope", "org.jetbrains.compose.web.attributes.builders.SelectAttrsScope")
)
typealias SelectAttrsBuilder = SelectAttrsScope
class SelectAttrsScope : AttrsScopeBuilder<HTMLSelectElement>() {
fun onInput( fun onInput(
listener: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit listener: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit
) { ) {
listeners.add(SelectInputEventListener(INPUT, listener)) registerEventListener(SelectInputEventListener(INPUT, listener))
} }
fun onChange( fun onChange(
listener: (SyntheticChangeEvent<String?, HTMLSelectElement>) -> Unit listener: (SyntheticChangeEvent<String?, HTMLSelectElement>) -> Unit
) { ) {
listeners.add(SelectChangeEventListener(listener)) registerEventListener(SelectChangeEventListener(listener))
} }
} }

49
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/TextAreaAttrsBuilder.kt

@ -1,49 +0,0 @@
/*
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package org.jetbrains.compose.web.attributes.builders
import org.jetbrains.compose.web.attributes.*
import org.jetbrains.compose.web.events.SyntheticChangeEvent
import org.jetbrains.compose.web.events.SyntheticSelectEvent
import org.jetbrains.compose.web.events.SyntheticInputEvent
import org.w3c.dom.HTMLTextAreaElement
class TextAreaAttrsBuilder : AttrsBuilder<HTMLTextAreaElement>() {
fun value(value: String): AttrsBuilder<HTMLTextAreaElement> {
prop(setInputValue, value)
return this
}
fun defaultValue(value: String): AttrsBuilder<HTMLTextAreaElement> {
prop(setTextAreaDefaultValue, value)
return this
}
fun onInput(
listener: (SyntheticInputEvent<String, HTMLTextAreaElement>) -> Unit
) {
listeners.add(InputEventListener(INPUT, InputType.Text, listener))
}
fun onChange(
listener: (SyntheticChangeEvent<String, HTMLTextAreaElement>) -> Unit
) {
listeners.add(ChangeEventListener(InputType.Text, listener))
}
fun onBeforeInput(
listener: (SyntheticInputEvent<String, HTMLTextAreaElement>) -> Unit
) {
listeners.add(InputEventListener(BEFOREINPUT, InputType.Text, listener))
}
fun onSelect(
listener: (SyntheticSelectEvent<HTMLTextAreaElement>) -> Unit
) {
listeners.add(SelectEventListener(listener))
}
}

64
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/TextAreaAttrsScope.kt

@ -0,0 +1,64 @@
/*
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package org.jetbrains.compose.web.attributes.builders
import org.jetbrains.compose.web.attributes.AttrsScope
import org.jetbrains.compose.web.attributes.AttrsScopeBuilder
import org.jetbrains.compose.web.attributes.ChangeEventListener
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.BEFOREINPUT
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.INPUT
import org.jetbrains.compose.web.attributes.InputEventListener
import org.jetbrains.compose.web.attributes.InputType
import org.jetbrains.compose.web.attributes.SelectEventListener
import org.jetbrains.compose.web.attributes.setInputValue
import org.jetbrains.compose.web.attributes.setTextAreaDefaultValue
import org.jetbrains.compose.web.events.SyntheticChangeEvent
import org.jetbrains.compose.web.events.SyntheticInputEvent
import org.jetbrains.compose.web.events.SyntheticSelectEvent
import org.w3c.dom.HTMLTextAreaElement
@Deprecated(
message = "Renamed to TextAreaAttrsScope",
replaceWith = ReplaceWith("TextAreaAttrsScope", "org.jetbrains.compose.web.attributes.builders.TextAreaAttrsScope")
)
typealias TextAreaAttrsBuilder = TextAreaAttrsScope
class TextAreaAttrsScope : AttrsScopeBuilder<HTMLTextAreaElement>() {
fun value(value: String): AttrsScope<HTMLTextAreaElement> {
prop(setInputValue, value)
return this
}
fun defaultValue(value: String): AttrsScope<HTMLTextAreaElement> {
prop(setTextAreaDefaultValue, value)
return this
}
fun onInput(
listener: (SyntheticInputEvent<String, HTMLTextAreaElement>) -> Unit
) {
registerEventListener(InputEventListener(INPUT, InputType.Text, listener))
}
fun onChange(
listener: (SyntheticChangeEvent<String, HTMLTextAreaElement>) -> Unit
) {
registerEventListener(ChangeEventListener(InputType.Text, listener))
}
fun onBeforeInput(
listener: (SyntheticInputEvent<String, HTMLTextAreaElement>) -> Unit
) {
registerEventListener(InputEventListener(BEFOREINPUT, InputType.Text, listener))
}
fun onSelect(
listener: (SyntheticSelectEvent<HTMLTextAreaElement>) -> Unit
) {
registerEventListener(SelectEventListener(listener))
}
}

2
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/CSSRules.kt

@ -4,7 +4,7 @@ import org.jetbrains.compose.web.css.selectors.CSSSelector
interface CSSStyleRuleBuilder : StyleBuilder interface CSSStyleRuleBuilder : StyleBuilder
open class CSSRuleBuilderImpl : CSSStyleRuleBuilder, StyleBuilderImpl() open class CSSRuleBuilderImpl : CSSStyleRuleBuilder, StyleScopeBuilder()
@Suppress("EqualsOrHashCode") @Suppress("EqualsOrHashCode")
interface CSSRuleDeclaration { interface CSSRuleDeclaration {

23
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleBuilder.kt → web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleScope.kt

@ -11,7 +11,7 @@ import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
/** /**
* StyleBuilder serves for two main purposes. Passed as a builder context (in [AttrsBuilder]), it * StyleBuilder serves for two main purposes. Passed as a builder context (in [AttrsScope]), it
* makes it possible to: * makes it possible to:
* 1. Add inlined css properties to the element (@see [property]) * 1. Add inlined css properties to the element (@see [property])
* 2. Set values to CSS variables (@see [variable]) * 2. Set values to CSS variables (@see [variable])
@ -125,13 +125,26 @@ fun <TValue : StylePropertyValue> variable() =
} }
interface StyleHolder { interface StyleHolder {
@ComposeWebInternalApi
val properties: StylePropertyList val properties: StylePropertyList
@ComposeWebInternalApi
val variables: StylePropertyList val variables: StylePropertyList
} }
interface StyleScope : StyleBuilder, StyleHolder {
@ComposeWebInternalApi
fun copyFrom(sb: StyleScope)
}
@Deprecated(
message = "Renamed to StyleScopeBuilder",
replaceWith = ReplaceWith("StyleScopeBuilder", "org.jetbrains.compose.web.css.StyleScopeBuilder")
)
typealias StyleBuilderImpl = StyleScopeBuilder
@OptIn(ComposeWebInternalApi::class) @OptIn(ComposeWebInternalApi::class)
@Suppress("EqualsOrHashCode") @Suppress("EqualsOrHashCode")
open class StyleBuilderImpl : StyleBuilder, StyleHolder { open class StyleScopeBuilder : StyleScope {
override val properties: MutableStylePropertyList = mutableListOf() override val properties: MutableStylePropertyList = mutableListOf()
override val variables: MutableStylePropertyList = mutableListOf() override val variables: MutableStylePropertyList = mutableListOf()
@ -147,11 +160,11 @@ open class StyleBuilderImpl : StyleBuilder, StyleHolder {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean {
return if (other is StyleHolder) { return if (other is StyleHolder) {
properties.nativeEquals(other.properties) && properties.nativeEquals(other.properties) &&
variables.nativeEquals(other.variables) variables.nativeEquals(other.variables)
} else false } else false
} }
internal fun copyFrom(sb: StyleBuilderImpl) { override fun copyFrom(sb: StyleScope) {
properties.addAll(sb.properties) properties.addAll(sb.properties)
variables.addAll(sb.variables) variables.addAll(sb.variables)
} }
@ -174,6 +187,6 @@ internal fun StylePropertyList.nativeEquals(properties: StylePropertyList): Bool
return all { prop -> return all { prop ->
val otherProp = properties[index++] val otherProp = properties[index++]
prop.name == otherProp.name && prop.name == otherProp.name &&
prop.value.toString() == otherProp.value.toString() prop.value.toString() == otherProp.value.toString()
} }
} }

21
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Base.kt

@ -1,8 +1,9 @@
package org.jetbrains.compose.web.dom package org.jetbrains.compose.web.dom
import androidx.compose.runtime.* import androidx.compose.runtime.*
import org.jetbrains.compose.web.attributes.AttrsBuilder import org.jetbrains.compose.web.attributes.AttrsScope
import org.jetbrains.compose.web.ExperimentalComposeWebApi import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.attributes.AttrsScopeBuilder
import org.jetbrains.compose.web.css.StyleHolder import org.jetbrains.compose.web.css.StyleHolder
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.jetbrains.compose.web.internal.runtime.DomNodeWrapper import org.jetbrains.compose.web.internal.runtime.DomNodeWrapper
@ -95,7 +96,7 @@ private class DomElementWrapper(override val node: Element): DomNodeWrapper(node
@Composable @Composable
fun <TElement : Element> TagElement( fun <TElement : Element> TagElement(
elementBuilder: ElementBuilder<TElement>, elementBuilder: ElementBuilder<TElement>,
applyAttrs: (AttrsBuilder<TElement>.() -> Unit)?, applyAttrs: (AttrsScope<TElement>.() -> Unit)?,
content: (@Composable ElementScope<TElement>.() -> Unit)? content: (@Composable ElementScope<TElement>.() -> Unit)?
) { ) {
val scope = remember { ElementScopeImpl<TElement>() } val scope = remember { ElementScopeImpl<TElement>() }
@ -108,16 +109,16 @@ fun <TElement : Element> TagElement(
DomElementWrapper(node) DomElementWrapper(node)
}, },
attrsSkippableUpdate = { attrsSkippableUpdate = {
val attrsBuilder = AttrsBuilder<TElement>() val attrsScope = AttrsScopeBuilder<TElement>()
applyAttrs?.invoke(attrsBuilder) applyAttrs?.invoke(attrsScope)
refEffect = attrsBuilder.refEffect refEffect = attrsScope.refEffect
update { update {
set(attrsBuilder.collect(), DomElementWrapper::updateAttrs) set(attrsScope.collect(), DomElementWrapper::updateAttrs)
set(attrsBuilder.collectListeners(), DomElementWrapper::updateEventListeners) set(attrsScope.collectListeners(), DomElementWrapper::updateEventListeners)
set(attrsBuilder.propertyUpdates, DomElementWrapper::updateProperties) set(attrsScope.propertyUpdates, DomElementWrapper::updateProperties)
set(attrsBuilder.styleBuilder, DomElementWrapper::updateStyleDeclarations) set(attrsScope.styleScope, DomElementWrapper::updateStyleDeclarations)
} }
}, },
elementScope = scope, elementScope = scope,
@ -135,7 +136,7 @@ fun <TElement : Element> TagElement(
@ExperimentalComposeWebApi @ExperimentalComposeWebApi
fun <TElement : Element> TagElement( fun <TElement : Element> TagElement(
tagName: String, tagName: String,
applyAttrs: AttrsBuilder<TElement>.() -> Unit, applyAttrs: AttrsScope<TElement>.() -> Unit,
content: (@Composable ElementScope<TElement>.() -> Unit)? content: (@Composable ElementScope<TElement>.() -> Unit)?
) = TagElement( ) = TagElement(
elementBuilder = ElementBuilder.createBuilder(tagName), elementBuilder = ElementBuilder.createBuilder(tagName),

153
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Elements.kt

@ -1,16 +1,16 @@
package org.jetbrains.compose.web.dom package org.jetbrains.compose.web.dom
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.web.attributes.SelectAttrsBuilder import androidx.compose.web.attributes.SelectAttrsScope
import kotlinx.browser.document import kotlinx.browser.document
import org.jetbrains.compose.web.attributes.* import org.jetbrains.compose.web.attributes.*
import org.jetbrains.compose.web.attributes.builders.* import org.jetbrains.compose.web.attributes.builders.*
import org.jetbrains.compose.web.css.CSSRuleDeclarationList import org.jetbrains.compose.web.css.CSSRuleDeclarationList
import org.jetbrains.compose.web.css.StyleSheetBuilder import org.jetbrains.compose.web.css.StyleSheetBuilder
import org.jetbrains.compose.web.css.StyleSheetBuilderImpl import org.jetbrains.compose.web.css.StyleSheetBuilderImpl
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.jetbrains.compose.web.internal.runtime.DomApplier import org.jetbrains.compose.web.internal.runtime.DomApplier
import org.jetbrains.compose.web.internal.runtime.DomNodeWrapper import org.jetbrains.compose.web.internal.runtime.DomNodeWrapper
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLAnchorElement import org.w3c.dom.HTMLAnchorElement
import org.w3c.dom.HTMLAreaElement import org.w3c.dom.HTMLAreaElement
@ -23,8 +23,8 @@ import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLEmbedElement import org.w3c.dom.HTMLEmbedElement
import org.w3c.dom.HTMLFieldSetElement import org.w3c.dom.HTMLFieldSetElement
import org.w3c.dom.HTMLFormElement import org.w3c.dom.HTMLFormElement
import org.w3c.dom.HTMLHeadingElement
import org.w3c.dom.HTMLHRElement import org.w3c.dom.HTMLHRElement
import org.w3c.dom.HTMLHeadingElement
import org.w3c.dom.HTMLIFrameElement import org.w3c.dom.HTMLIFrameElement
import org.w3c.dom.HTMLImageElement import org.w3c.dom.HTMLImageElement
import org.w3c.dom.HTMLInputElement import org.w3c.dom.HTMLInputElement
@ -60,7 +60,12 @@ import org.w3c.dom.HTMLVideoElement
import org.w3c.dom.Text import org.w3c.dom.Text
import org.w3c.dom.css.CSSStyleSheet import org.w3c.dom.css.CSSStyleSheet
typealias AttrBuilderContext<T> = AttrsBuilder<T>.() -> Unit @Deprecated(
message = "Renamed to AttrsBuilder<T>",
replaceWith = ReplaceWith("AttrsBuilder<T>", "org.jetbrains.compose.web.dom.AttrsBuilder")
)
typealias AttrBuilderContext<T> = AttrsBuilder<T>
typealias AttrsBuilder<T> = AttrsScope<T>.() -> Unit
typealias ContentBuilder<T> = @Composable ElementScope<T>.() -> Unit typealias ContentBuilder<T> = @Composable ElementScope<T>.() -> Unit
private open class ElementBuilderImplementation<TElement : Element>(private val tagName: String) : ElementBuilder<TElement> { private open class ElementBuilderImplementation<TElement : Element>(private val tagName: String) : ElementBuilder<TElement> {
@ -163,7 +168,7 @@ fun interface ElementBuilder<TElement : Element> {
@Composable @Composable
fun Address( fun Address(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
@ -175,7 +180,7 @@ fun Address(
@Composable @Composable
fun Article( fun Article(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
@ -187,7 +192,7 @@ fun Article(
@Composable @Composable
fun Aside( fun Aside(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
@ -199,7 +204,7 @@ fun Aside(
@Composable @Composable
fun Header( fun Header(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
@ -211,7 +216,7 @@ fun Header(
@Composable @Composable
fun Area( fun Area(
attrs: AttrBuilderContext<HTMLAreaElement>? = null, attrs: AttrsBuilder<HTMLAreaElement>? = null,
content: ContentBuilder<HTMLAreaElement>? = null content: ContentBuilder<HTMLAreaElement>? = null
) { ) {
TagElement( TagElement(
@ -223,7 +228,7 @@ fun Area(
@Composable @Composable
fun Audio( fun Audio(
attrs: AttrBuilderContext<HTMLAudioElement>? = null, attrs: AttrsBuilder<HTMLAudioElement>? = null,
content: ContentBuilder<HTMLAudioElement>? = null content: ContentBuilder<HTMLAudioElement>? = null
) { ) {
TagElement( TagElement(
@ -235,7 +240,7 @@ fun Audio(
@Composable @Composable
fun HTMLMap( fun HTMLMap(
attrs: AttrBuilderContext<HTMLMapElement>? = null, attrs: AttrsBuilder<HTMLMapElement>? = null,
content: ContentBuilder<HTMLMapElement>? = null content: ContentBuilder<HTMLMapElement>? = null
) { ) {
TagElement( TagElement(
@ -247,7 +252,7 @@ fun HTMLMap(
@Composable @Composable
fun Track( fun Track(
attrs: AttrBuilderContext<HTMLTrackElement>? = null, attrs: AttrsBuilder<HTMLTrackElement>? = null,
content: ContentBuilder<HTMLTrackElement>? = null content: ContentBuilder<HTMLTrackElement>? = null
) { ) {
TagElement( TagElement(
@ -259,7 +264,7 @@ fun Track(
@Composable @Composable
fun Video( fun Video(
attrs: AttrBuilderContext<HTMLVideoElement>? = null, attrs: AttrsBuilder<HTMLVideoElement>? = null,
content: ContentBuilder<HTMLVideoElement>? = null content: ContentBuilder<HTMLVideoElement>? = null
) { ) {
TagElement( TagElement(
@ -271,7 +276,7 @@ fun Video(
@Composable @Composable
fun Datalist( fun Datalist(
attrs: AttrBuilderContext<HTMLDataListElement>? = null, attrs: AttrsBuilder<HTMLDataListElement>? = null,
content: ContentBuilder<HTMLDataListElement>? = null content: ContentBuilder<HTMLDataListElement>? = null
) { ) {
TagElement( TagElement(
@ -283,7 +288,7 @@ fun Datalist(
@Composable @Composable
fun Fieldset( fun Fieldset(
attrs: AttrBuilderContext<HTMLFieldSetElement>? = null, attrs: AttrsBuilder<HTMLFieldSetElement>? = null,
content: ContentBuilder<HTMLFieldSetElement>? = null content: ContentBuilder<HTMLFieldSetElement>? = null
) { ) {
TagElement( TagElement(
@ -295,7 +300,7 @@ fun Fieldset(
@Composable @Composable
fun Legend( fun Legend(
attrs: AttrBuilderContext<HTMLLegendElement>? = null, attrs: AttrsBuilder<HTMLLegendElement>? = null,
content: ContentBuilder<HTMLLegendElement>? = null content: ContentBuilder<HTMLLegendElement>? = null
) { ) {
TagElement( TagElement(
@ -307,7 +312,7 @@ fun Legend(
@Composable @Composable
fun Meter( fun Meter(
attrs: AttrBuilderContext<HTMLMeterElement>? = null, attrs: AttrsBuilder<HTMLMeterElement>? = null,
content: ContentBuilder<HTMLMeterElement>? = null content: ContentBuilder<HTMLMeterElement>? = null
) { ) {
TagElement( TagElement(
@ -319,7 +324,7 @@ fun Meter(
@Composable @Composable
fun Output( fun Output(
attrs: AttrBuilderContext<HTMLOutputElement>? = null, attrs: AttrsBuilder<HTMLOutputElement>? = null,
content: ContentBuilder<HTMLOutputElement>? = null content: ContentBuilder<HTMLOutputElement>? = null
) { ) {
TagElement( TagElement(
@ -331,7 +336,7 @@ fun Output(
@Composable @Composable
fun Progress( fun Progress(
attrs: AttrBuilderContext<HTMLProgressElement>? = null, attrs: AttrsBuilder<HTMLProgressElement>? = null,
content: ContentBuilder<HTMLProgressElement>? = null content: ContentBuilder<HTMLProgressElement>? = null
) { ) {
TagElement( TagElement(
@ -343,7 +348,7 @@ fun Progress(
@Composable @Composable
fun Embed( fun Embed(
attrs: AttrBuilderContext<HTMLEmbedElement>? = null, attrs: AttrsBuilder<HTMLEmbedElement>? = null,
content: ContentBuilder<HTMLEmbedElement>? = null content: ContentBuilder<HTMLEmbedElement>? = null
) { ) {
TagElement( TagElement(
@ -355,7 +360,7 @@ fun Embed(
@Composable @Composable
fun Iframe( fun Iframe(
attrs: AttrBuilderContext<HTMLIFrameElement>? = null, attrs: AttrsBuilder<HTMLIFrameElement>? = null,
content: ContentBuilder<HTMLIFrameElement>? = null content: ContentBuilder<HTMLIFrameElement>? = null
) { ) {
TagElement( TagElement(
@ -367,7 +372,7 @@ fun Iframe(
@Composable @Composable
fun Object( fun Object(
attrs: AttrBuilderContext<HTMLObjectElement>? = null, attrs: AttrsBuilder<HTMLObjectElement>? = null,
content: ContentBuilder<HTMLObjectElement>? = null content: ContentBuilder<HTMLObjectElement>? = null
) { ) {
TagElement( TagElement(
@ -379,7 +384,7 @@ fun Object(
@Composable @Composable
fun Param( fun Param(
attrs: AttrBuilderContext<HTMLParamElement>? = null, attrs: AttrsBuilder<HTMLParamElement>? = null,
content: ContentBuilder<HTMLParamElement>? = null content: ContentBuilder<HTMLParamElement>? = null
) { ) {
TagElement( TagElement(
@ -391,7 +396,7 @@ fun Param(
@Composable @Composable
fun Picture( fun Picture(
attrs: AttrBuilderContext<HTMLPictureElement>? = null, attrs: AttrsBuilder<HTMLPictureElement>? = null,
content: ContentBuilder<HTMLPictureElement>? = null content: ContentBuilder<HTMLPictureElement>? = null
) { ) {
TagElement( TagElement(
@ -403,7 +408,7 @@ fun Picture(
@Composable @Composable
fun Source( fun Source(
attrs: AttrBuilderContext<HTMLSourceElement>? = null, attrs: AttrsBuilder<HTMLSourceElement>? = null,
content: ContentBuilder<HTMLSourceElement>? = null content: ContentBuilder<HTMLSourceElement>? = null
) { ) {
TagElement( TagElement(
@ -426,7 +431,7 @@ fun Text(value: String) {
@Composable @Composable
fun Div( fun Div(
attrs: AttrBuilderContext<HTMLDivElement>? = null, attrs: AttrsBuilder<HTMLDivElement>? = null,
content: ContentBuilder<HTMLDivElement>? = null content: ContentBuilder<HTMLDivElement>? = null
) { ) {
TagElement( TagElement(
@ -439,7 +444,7 @@ fun Div(
@Composable @Composable
fun A( fun A(
href: String? = null, href: String? = null,
attrs: AttrBuilderContext<HTMLAnchorElement>? = null, attrs: AttrsBuilder<HTMLAnchorElement>? = null,
content: ContentBuilder<HTMLAnchorElement>? = null content: ContentBuilder<HTMLAnchorElement>? = null
) { ) {
TagElement( TagElement(
@ -458,101 +463,101 @@ fun A(
@Composable @Composable
fun Button( fun Button(
attrs: AttrBuilderContext<HTMLButtonElement>? = null, attrs: AttrsBuilder<HTMLButtonElement>? = null,
content: ContentBuilder<HTMLButtonElement>? = null content: ContentBuilder<HTMLButtonElement>? = null
) = TagElement(elementBuilder = Button, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = Button, applyAttrs = attrs, content = content)
@Composable @Composable
fun H1( fun H1(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null, attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H1, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = H1, applyAttrs = attrs, content = content)
@Composable @Composable
fun H2( fun H2(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null, attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H2, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = H2, applyAttrs = attrs, content = content)
@Composable @Composable
fun H3( fun H3(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null, attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H3, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = H3, applyAttrs = attrs, content = content)
@Composable @Composable
fun H4( fun H4(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null, attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H4, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = H4, applyAttrs = attrs, content = content)
@Composable @Composable
fun H5( fun H5(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null, attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H5, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = H5, applyAttrs = attrs, content = content)
@Composable @Composable
fun H6( fun H6(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null, attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H6, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = H6, applyAttrs = attrs, content = content)
@Composable @Composable
fun P( fun P(
attrs: AttrBuilderContext<HTMLParagraphElement>? = null, attrs: AttrsBuilder<HTMLParagraphElement>? = null,
content: ContentBuilder<HTMLParagraphElement>? = null content: ContentBuilder<HTMLParagraphElement>? = null
) = TagElement(elementBuilder = P, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = P, applyAttrs = attrs, content = content)
@Composable @Composable
fun Em( fun Em(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = Em, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = Em, applyAttrs = attrs, content = content)
@Composable @Composable
fun I( fun I(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = I, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = I, applyAttrs = attrs, content = content)
@Composable @Composable
fun B( fun B(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = B, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = B, applyAttrs = attrs, content = content)
@Composable @Composable
fun Small( fun Small(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = Small, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = Small, applyAttrs = attrs, content = content)
@Composable @Composable
fun Span( fun Span(
attrs: AttrBuilderContext<HTMLSpanElement>? = null, attrs: AttrsBuilder<HTMLSpanElement>? = null,
content: ContentBuilder<HTMLSpanElement>? = null content: ContentBuilder<HTMLSpanElement>? = null
) = TagElement(elementBuilder = Span, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = Span, applyAttrs = attrs, content = content)
@Composable @Composable
fun Br(attrs: AttrBuilderContext<HTMLBRElement>? = null) = fun Br(attrs: AttrsBuilder<HTMLBRElement>? = null) =
TagElement(elementBuilder = Br, applyAttrs = attrs, content = null) TagElement(elementBuilder = Br, applyAttrs = attrs, content = null)
@Composable @Composable
fun Ul( fun Ul(
attrs: AttrBuilderContext<HTMLUListElement>? = null, attrs: AttrsBuilder<HTMLUListElement>? = null,
content: ContentBuilder<HTMLUListElement>? = null content: ContentBuilder<HTMLUListElement>? = null
) = TagElement(elementBuilder = Ul, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = Ul, applyAttrs = attrs, content = content)
@Composable @Composable
fun Ol( fun Ol(
attrs: AttrBuilderContext<HTMLOListElement>? = null, attrs: AttrsBuilder<HTMLOListElement>? = null,
content: ContentBuilder<HTMLOListElement>? = null content: ContentBuilder<HTMLOListElement>? = null
) = TagElement(elementBuilder = Ol, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = Ol, applyAttrs = attrs, content = content)
@Composable @Composable
fun Li( fun Li(
attrs: AttrBuilderContext<HTMLLIElement>? = null, attrs: AttrsBuilder<HTMLLIElement>? = null,
content: ContentBuilder<HTMLLIElement>? = null content: ContentBuilder<HTMLLIElement>? = null
) = TagElement(elementBuilder = Li, applyAttrs = attrs, content = content) ) = TagElement(elementBuilder = Li, applyAttrs = attrs, content = content)
@ -560,7 +565,7 @@ fun Li(
fun Img( fun Img(
src: String, src: String,
alt: String = "", alt: String = "",
attrs: AttrBuilderContext<HTMLImageElement>? = null attrs: AttrsBuilder<HTMLImageElement>? = null
) = TagElement( ) = TagElement(
elementBuilder = Img, elementBuilder = Img,
applyAttrs = { applyAttrs = {
@ -575,7 +580,7 @@ fun Img(
@Composable @Composable
fun Form( fun Form(
action: String? = null, action: String? = null,
attrs: AttrBuilderContext<HTMLFormElement>? = null, attrs: AttrsBuilder<HTMLFormElement>? = null,
content: ContentBuilder<HTMLFormElement>? = null content: ContentBuilder<HTMLFormElement>? = null
) = TagElement( ) = TagElement(
elementBuilder = Form, elementBuilder = Form,
@ -590,7 +595,7 @@ fun Form(
@Composable @Composable
fun Select( fun Select(
attrs: (SelectAttrsBuilder.() -> Unit)? = null, attrs: (SelectAttrsScope.() -> Unit)? = null,
multiple: Boolean = false, multiple: Boolean = false,
content: ContentBuilder<HTMLSelectElement>? = null content: ContentBuilder<HTMLSelectElement>? = null
) = TagElement( ) = TagElement(
@ -598,7 +603,7 @@ fun Select(
applyAttrs = { applyAttrs = {
if (multiple) multiple() if (multiple) multiple()
if (attrs != null) { if (attrs != null) {
val selectAttrsBuilder = with(SelectAttrsBuilder()) { val selectAttrsBuilder = with(SelectAttrsScope()) {
attrs() attrs()
this this
} }
@ -611,7 +616,7 @@ fun Select(
@Composable @Composable
fun Option( fun Option(
value: String, value: String,
attrs: AttrBuilderContext<HTMLOptionElement>? = null, attrs: AttrsBuilder<HTMLOptionElement>? = null,
content: ContentBuilder<HTMLOptionElement>? = null content: ContentBuilder<HTMLOptionElement>? = null
) = TagElement( ) = TagElement(
elementBuilder = Option, elementBuilder = Option,
@ -627,7 +632,7 @@ fun Option(
@Composable @Composable
fun OptGroup( fun OptGroup(
label: String, label: String,
attrs: AttrBuilderContext<HTMLOptGroupElement>? = null, attrs: AttrsBuilder<HTMLOptGroupElement>? = null,
content: ContentBuilder<HTMLOptGroupElement>? = null content: ContentBuilder<HTMLOptGroupElement>? = null
) = TagElement( ) = TagElement(
elementBuilder = OptGroup, elementBuilder = OptGroup,
@ -642,7 +647,7 @@ fun OptGroup(
@Composable @Composable
fun Section( fun Section(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) = TagElement( ) = TagElement(
elementBuilder = Section, elementBuilder = Section,
@ -671,7 +676,7 @@ fun Section(
@Composable @Composable
fun TextArea( fun TextArea(
value: String? = null, value: String? = null,
attrs: (TextAreaAttrsBuilder.() -> Unit)? = null attrs: (TextAreaAttrsScope.() -> Unit)? = null
) { ) {
// if firstProvidedValueWasNotNull then TextArea behaves as controlled input // if firstProvidedValueWasNotNull then TextArea behaves as controlled input
val firstProvidedValueWasNotNull = remember { value != null } val firstProvidedValueWasNotNull = remember { value != null }
@ -682,7 +687,7 @@ fun TextArea(
TagElement( TagElement(
elementBuilder = TextArea, elementBuilder = TextArea,
applyAttrs = { applyAttrs = {
val textAreaAttrsBuilder = TextAreaAttrsBuilder() val textAreaAttrsBuilder = TextAreaAttrsScope()
textAreaAttrsBuilder.onInput { textAreaAttrsBuilder.onInput {
// controlled state needs to be restored after every input // controlled state needs to be restored after every input
keyForRestoringControlledState.value = keyForRestoringControlledState.value + 1 keyForRestoringControlledState.value = keyForRestoringControlledState.value + 1
@ -708,7 +713,7 @@ private val textAreaRestoreControlledStateEffect: DomEffectScope.(HTMLTextAreaEl
@Composable @Composable
fun Nav( fun Nav(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) = TagElement( ) = TagElement(
elementBuilder = Nav, elementBuilder = Nav,
@ -718,7 +723,7 @@ fun Nav(
@Composable @Composable
fun Pre( fun Pre(
attrs: AttrBuilderContext<HTMLPreElement>? = null, attrs: AttrsBuilder<HTMLPreElement>? = null,
content: ContentBuilder<HTMLPreElement>? = null content: ContentBuilder<HTMLPreElement>? = null
) { ) {
TagElement( TagElement(
@ -730,7 +735,7 @@ fun Pre(
@Composable @Composable
fun Code( fun Code(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
@ -742,7 +747,7 @@ fun Code(
@Composable @Composable
fun Main( fun Main(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
@ -754,7 +759,7 @@ fun Main(
@Composable @Composable
fun Footer( fun Footer(
attrs: AttrBuilderContext<HTMLElement>? = null, attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
@ -766,7 +771,7 @@ fun Footer(
@Composable @Composable
fun Hr( fun Hr(
attrs: AttrBuilderContext<HTMLHRElement>? = null attrs: AttrsBuilder<HTMLHRElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Hr, elementBuilder = Hr,
@ -778,7 +783,7 @@ fun Hr(
@Composable @Composable
fun Label( fun Label(
forId: String? = null, forId: String? = null,
attrs: AttrBuilderContext<HTMLLabelElement>? = null, attrs: AttrsBuilder<HTMLLabelElement>? = null,
content: ContentBuilder<HTMLLabelElement>? = null content: ContentBuilder<HTMLLabelElement>? = null
) { ) {
TagElement( TagElement(
@ -797,7 +802,7 @@ fun Label(
@Composable @Composable
fun Table( fun Table(
attrs: AttrBuilderContext<HTMLTableElement>? = null, attrs: AttrsBuilder<HTMLTableElement>? = null,
content: ContentBuilder<HTMLTableElement>? = null content: ContentBuilder<HTMLTableElement>? = null
) { ) {
TagElement( TagElement(
@ -809,7 +814,7 @@ fun Table(
@Composable @Composable
fun Caption( fun Caption(
attrs: AttrBuilderContext<HTMLTableCaptionElement>? = null, attrs: AttrsBuilder<HTMLTableCaptionElement>? = null,
content: ContentBuilder<HTMLTableCaptionElement>? = null content: ContentBuilder<HTMLTableCaptionElement>? = null
) { ) {
TagElement( TagElement(
@ -821,7 +826,7 @@ fun Caption(
@Composable @Composable
fun Col( fun Col(
attrs: AttrBuilderContext<HTMLTableColElement>? = null attrs: AttrsBuilder<HTMLTableColElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Col, elementBuilder = Col,
@ -832,7 +837,7 @@ fun Col(
@Composable @Composable
fun Colgroup( fun Colgroup(
attrs: AttrBuilderContext<HTMLTableColElement>? = null, attrs: AttrsBuilder<HTMLTableColElement>? = null,
content: ContentBuilder<HTMLTableColElement>? = null content: ContentBuilder<HTMLTableColElement>? = null
) { ) {
TagElement( TagElement(
@ -844,7 +849,7 @@ fun Colgroup(
@Composable @Composable
fun Tr( fun Tr(
attrs: AttrBuilderContext<HTMLTableRowElement>? = null, attrs: AttrsBuilder<HTMLTableRowElement>? = null,
content: ContentBuilder<HTMLTableRowElement>? = null content: ContentBuilder<HTMLTableRowElement>? = null
) { ) {
TagElement( TagElement(
@ -856,7 +861,7 @@ fun Tr(
@Composable @Composable
fun Thead( fun Thead(
attrs: AttrBuilderContext<HTMLTableSectionElement>? = null, attrs: AttrsBuilder<HTMLTableSectionElement>? = null,
content: ContentBuilder<HTMLTableSectionElement>? = null content: ContentBuilder<HTMLTableSectionElement>? = null
) { ) {
TagElement( TagElement(
@ -868,7 +873,7 @@ fun Thead(
@Composable @Composable
fun Th( fun Th(
attrs: AttrBuilderContext<HTMLTableCellElement>? = null, attrs: AttrsBuilder<HTMLTableCellElement>? = null,
content: ContentBuilder<HTMLTableCellElement>? = null content: ContentBuilder<HTMLTableCellElement>? = null
) { ) {
TagElement( TagElement(
@ -880,7 +885,7 @@ fun Th(
@Composable @Composable
fun Td( fun Td(
attrs: AttrBuilderContext<HTMLTableCellElement>? = null, attrs: AttrsBuilder<HTMLTableCellElement>? = null,
content: ContentBuilder<HTMLTableCellElement>? = null content: ContentBuilder<HTMLTableCellElement>? = null
) { ) {
TagElement( TagElement(
@ -892,7 +897,7 @@ fun Td(
@Composable @Composable
fun Tbody( fun Tbody(
attrs: AttrBuilderContext<HTMLTableSectionElement>? = null, attrs: AttrsBuilder<HTMLTableSectionElement>? = null,
content: ContentBuilder<HTMLTableSectionElement>? = null content: ContentBuilder<HTMLTableSectionElement>? = null
) { ) {
TagElement( TagElement(
@ -904,7 +909,7 @@ fun Tbody(
@Composable @Composable
fun Tfoot( fun Tfoot(
attrs: AttrBuilderContext<HTMLTableSectionElement>? = null, attrs: AttrsBuilder<HTMLTableSectionElement>? = null,
content: ContentBuilder<HTMLTableSectionElement>? = null content: ContentBuilder<HTMLTableSectionElement>? = null
) { ) {
TagElement( TagElement(
@ -922,7 +927,7 @@ fun Tfoot(
*/ */
@Composable @Composable
fun Style( fun Style(
applyAttrs: (AttrsBuilder<HTMLStyleElement>.() -> Unit)? = null, applyAttrs: (AttrsScope<HTMLStyleElement>.() -> Unit)? = null,
cssRules: CSSRuleDeclarationList cssRules: CSSRuleDeclarationList
) { ) {
TagElement( TagElement(
@ -951,7 +956,7 @@ fun Style(
*/ */
@Composable @Composable
inline fun Style( inline fun Style(
noinline applyAttrs: (AttrsBuilder<HTMLStyleElement>.() -> Unit)? = null, noinline applyAttrs: (AttrsScope<HTMLStyleElement>.() -> Unit)? = null,
rulesBuild: StyleSheetBuilder.() -> Unit rulesBuild: StyleSheetBuilder.() -> Unit
) { ) {
val builder = StyleSheetBuilderImpl() val builder = StyleSheetBuilderImpl()
@ -994,7 +999,7 @@ inline fun Style(
@Composable @Composable
fun <K> Input( fun <K> Input(
type: InputType<K>, type: InputType<K>,
attrs: InputAttrsBuilder<K>.() -> Unit attrs: InputAttrsScope<K>.() -> Unit
) { ) {
// changes to this key trigger [inputRestoreControlledStateEffect] // changes to this key trigger [inputRestoreControlledStateEffect]
val keyForRestoringControlledState: MutableState<Int> = remember { mutableStateOf(0) } val keyForRestoringControlledState: MutableState<Int> = remember { mutableStateOf(0) }
@ -1002,7 +1007,7 @@ fun <K> Input(
TagElement( TagElement(
elementBuilder = Input, elementBuilder = Input,
applyAttrs = { applyAttrs = {
val inputAttrsBuilder = InputAttrsBuilder(type) val inputAttrsBuilder = InputAttrsScope(type)
inputAttrsBuilder.type(type) inputAttrsBuilder.type(type)
inputAttrsBuilder.onInput { inputAttrsBuilder.onInput {
// controlled state needs to be restored after every input // controlled state needs to be restored after every input

42
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/InputElements.kt

@ -2,12 +2,12 @@ package org.jetbrains.compose.web.dom
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.NonRestartableComposable import androidx.compose.runtime.NonRestartableComposable
import org.jetbrains.compose.web.attributes.builders.InputAttrsBuilder import org.jetbrains.compose.web.attributes.builders.InputAttrsScope
import org.jetbrains.compose.web.attributes.* import org.jetbrains.compose.web.attributes.*
private fun InputAttrsBuilder<String>.applyAttrsWithStringValue( private fun InputAttrsScope<String>.applyAttrsWithStringValue(
value: String, value: String,
attrs: InputAttrsBuilder<String>.() -> Unit attrs: InputAttrsScope<String>.() -> Unit
) { ) {
value(value) value(value)
attrs() attrs()
@ -22,7 +22,7 @@ private fun InputAttrsBuilder<String>.applyAttrsWithStringValue(
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun CheckboxInput(checked: Boolean = false, attrs: InputAttrsBuilder<Boolean>.() -> Unit = {}) { fun CheckboxInput(checked: Boolean = false, attrs: InputAttrsScope<Boolean>.() -> Unit = {}) {
Input( Input(
type = InputType.Checkbox, type = InputType.Checkbox,
attrs = { attrs = {
@ -41,7 +41,7 @@ fun CheckboxInput(checked: Boolean = false, attrs: InputAttrsBuilder<Boolean>.()
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun DateInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun DateInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Date, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Date, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -54,7 +54,7 @@ fun DateInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun DateTimeLocalInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun DateTimeLocalInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.DateTimeLocal, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.DateTimeLocal, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -67,7 +67,7 @@ fun DateTimeLocalInput(value: String = "", attrs: InputAttrsBuilder<String>.() -
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun EmailInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun EmailInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Email, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Email, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -80,7 +80,7 @@ fun EmailInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun FileInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun FileInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.File, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.File, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -93,7 +93,7 @@ fun FileInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun HiddenInput(attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun HiddenInput(attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Hidden, attrs = attrs) Input(type = InputType.Hidden, attrs = attrs)
} }
@ -106,7 +106,7 @@ fun HiddenInput(attrs: InputAttrsBuilder<String>.() -> Unit = {}) {
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun MonthInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun MonthInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Month, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Month, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -123,7 +123,7 @@ fun NumberInput(
value: Number? = null, value: Number? = null,
min: Number? = null, min: Number? = null,
max: Number? = null, max: Number? = null,
attrs: InputAttrsBuilder<Number?>.() -> Unit = {} attrs: InputAttrsScope<Number?>.() -> Unit = {}
) { ) {
Input( Input(
type = InputType.Number, type = InputType.Number,
@ -145,7 +145,7 @@ fun NumberInput(
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun PasswordInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun PasswordInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Password, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Password, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -158,7 +158,7 @@ fun PasswordInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Uni
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun RadioInput(checked: Boolean = false, attrs: InputAttrsBuilder<Boolean>.() -> Unit = {}) { fun RadioInput(checked: Boolean = false, attrs: InputAttrsScope<Boolean>.() -> Unit = {}) {
Input( Input(
type = InputType.Radio, type = InputType.Radio,
attrs = { attrs = {
@ -182,7 +182,7 @@ fun RangeInput(
min: Number? = null, min: Number? = null,
max: Number? = null, max: Number? = null,
step: Number = 1, step: Number = 1,
attrs: InputAttrsBuilder<Number?>.() -> Unit = {} attrs: InputAttrsScope<Number?>.() -> Unit = {}
) { ) {
Input( Input(
type = InputType.Range, type = InputType.Range,
@ -205,7 +205,7 @@ fun RangeInput(
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun SearchInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun SearchInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Search, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Search, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -215,7 +215,7 @@ fun SearchInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun SubmitInput(attrs: InputAttrsBuilder<Unit>.() -> Unit = {}) { fun SubmitInput(attrs: InputAttrsScope<Unit>.() -> Unit = {}) {
Input(type = InputType.Submit, attrs = attrs) Input(type = InputType.Submit, attrs = attrs)
} }
@ -228,7 +228,7 @@ fun SubmitInput(attrs: InputAttrsBuilder<Unit>.() -> Unit = {}) {
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun TelInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun TelInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Tel, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Tel, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -241,7 +241,7 @@ fun TelInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun TextInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun TextInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Text, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Text, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -254,7 +254,7 @@ fun TextInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun TimeInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun TimeInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Time, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Time, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -267,7 +267,7 @@ fun TimeInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun UrlInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun UrlInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Url, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Url, attrs = { applyAttrsWithStringValue(value, attrs) })
} }
@ -280,6 +280,6 @@ fun UrlInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {
*/ */
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun WeekInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {}) { fun WeekInput(value: String = "", attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Week, attrs = { applyAttrsWithStringValue(value, attrs) }) Input(type = InputType.Week, attrs = { applyAttrsWithStringValue(value, attrs) })
} }

4
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/RadioGroup.kt

@ -3,10 +3,10 @@ package org.jetbrains.compose.web.dom
import androidx.compose.runtime.* import androidx.compose.runtime.*
import org.jetbrains.compose.web.ExperimentalComposeWebApi import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.attributes.InputType import org.jetbrains.compose.web.attributes.InputType
import org.jetbrains.compose.web.attributes.builders.InputAttrsBuilder import org.jetbrains.compose.web.attributes.builders.InputAttrsScope
import org.jetbrains.compose.web.attributes.name import org.jetbrains.compose.web.attributes.name
typealias RadioInputAttrsBuilder = (InputAttrsBuilder<Boolean>.() -> Unit) typealias RadioInputAttrsBuilder = (InputAttrsScope<Boolean>.() -> Unit)
/** /**
* @param value - sets `value` attribute * @param value - sets `value` attribute

42
web/core/src/jsTest/kotlin/elements/AttributesTests.kt

@ -21,7 +21,7 @@ class AttributesTests {
@Test @Test
fun copyFromStyleBuilderCopiesCorrectly() { fun copyFromStyleBuilderCopiesCorrectly() {
val copyFromStyleBuilder = StyleBuilderImpl().apply { val copyFromStyleBuilder = StyleScopeBuilder().apply {
property("color", "red") property("color", "red")
property("height", 100.px) property("height", 100.px)
@ -29,7 +29,7 @@ class AttributesTests {
variable("var2", 100.px) variable("var2", 100.px)
} }
val copyToStyleBuilder = StyleBuilderImpl().apply { val copyToStyleBuilder = StyleScopeBuilder().apply {
copyFrom(copyFromStyleBuilder) copyFrom(copyFromStyleBuilder)
} }
@ -38,7 +38,7 @@ class AttributesTests {
@Test @Test
fun copyFromAttrsBuilderCopiesCorrectly() { fun copyFromAttrsBuilderCopiesCorrectly() {
val attrsBuilderCopyFrom = AttrsBuilder<HTMLElement>().apply { val attrsScopeCopyFrom = AttrsScopeBuilder<HTMLElement>().apply {
id("id1") id("id1")
classes("a b c") classes("a b c")
attr("title", "customTitle") attr("title", "customTitle")
@ -56,54 +56,54 @@ class AttributesTests {
onMouseEnter { } onMouseEnter { }
} }
val copyToAttrsBuilder = AttrsBuilder<HTMLElement>().apply { val copyToAttrsScope = AttrsScopeBuilder<HTMLElement>().apply {
copyFrom(attrsBuilderCopyFrom) copyFrom(attrsScopeCopyFrom)
} }
assertEquals(attrsBuilderCopyFrom.attributesMap, copyToAttrsBuilder.attributesMap) assertEquals(attrsScopeCopyFrom.attributesMap, copyToAttrsScope.attributesMap)
assertEquals(attrsBuilderCopyFrom.styleBuilder, copyToAttrsBuilder.styleBuilder) assertEquals(attrsScopeCopyFrom.styleScope, copyToAttrsScope.styleScope)
assertEquals(attrsBuilderCopyFrom.refEffect, copyToAttrsBuilder.refEffect) assertEquals(attrsScopeCopyFrom.refEffect, copyToAttrsScope.refEffect)
assertEquals(attrsBuilderCopyFrom.propertyUpdates, copyToAttrsBuilder.propertyUpdates) assertEquals(attrsScopeCopyFrom.propertyUpdates, copyToAttrsScope.propertyUpdates)
assertEquals(attrsBuilderCopyFrom.collectListeners(), copyToAttrsBuilder.collectListeners()) assertEquals(attrsScopeCopyFrom.collectListeners(), copyToAttrsScope.collectListeners())
} }
@Test @Test
fun attrsBuilderCopyFromPreservesExistingAttrs() { fun attrsBuilderCopyFromPreservesExistingAttrs() {
val attrsBuilderCopyFrom = AttrsBuilder<HTMLElement>().apply { val attrsScopeCopyFrom = AttrsScopeBuilder<HTMLElement>().apply {
attr("title", "customTitle") attr("title", "customTitle")
} }
val copyToAttrsBuilder = AttrsBuilder<HTMLElement>().apply { val copyToAttrsScope = AttrsScopeBuilder<HTMLElement>().apply {
id("id1") id("id1")
onClick { } onClick { }
style { style {
width(100.px) width(100.px)
} }
copyFrom(attrsBuilderCopyFrom) copyFrom(attrsScopeCopyFrom)
} }
assertEquals("id1", copyToAttrsBuilder.attributesMap["id"]) assertEquals("id1", copyToAttrsScope.attributesMap["id"])
assertEquals(StyleBuilderImpl().apply { width(100.px) }, copyToAttrsBuilder.styleBuilder) assertEquals(StyleScopeBuilder().apply { width(100.px) }, copyToAttrsScope.styleScope)
val listeners = copyToAttrsBuilder.collectListeners() val listeners = copyToAttrsScope.collectListeners()
assertEquals(1, listeners.size) assertEquals(1, listeners.size)
assertEquals("click", listeners[0].event) assertEquals("click", listeners[0].event)
} }
@Test @Test
fun attrsBuilderCopyFromOverridesSameAttrs() { fun attrsBuilderCopyFromOverridesSameAttrs() {
val attrsBuilderCopyFrom = AttrsBuilder<HTMLElement>().apply { val attrsScopeCopyFrom = AttrsScopeBuilder<HTMLElement>().apply {
attr("title", "customTitleNew") attr("title", "customTitleNew")
} }
val copyToAttrsBuilder = AttrsBuilder<HTMLElement>().apply { val copyToAttrsScope = AttrsScopeBuilder<HTMLElement>().apply {
attr("title", "customTitleOld") attr("title", "customTitleOld")
} }
assertEquals("customTitleOld", copyToAttrsBuilder.attributesMap["title"]) assertEquals("customTitleOld", copyToAttrsScope.attributesMap["title"])
copyToAttrsBuilder.copyFrom(attrsBuilderCopyFrom) copyToAttrsScope.copyFrom(attrsScopeCopyFrom)
assertEquals("customTitleNew", copyToAttrsBuilder.attributesMap["title"]) assertEquals("customTitleNew", copyToAttrsScope.attributesMap["title"])
} }
@Test @Test

7
web/core/src/jsTest/kotlin/elements/ElementsTests.kt

@ -6,10 +6,9 @@
package org.jetbrains.compose.web.core.tests.elements package org.jetbrains.compose.web.core.tests.elements
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import kotlinx.browser.document import kotlinx.browser.document
import org.jetbrains.compose.web.ExperimentalComposeWebApi import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.attributes.AttrsBuilder import org.jetbrains.compose.web.attributes.AttrsScope
import org.jetbrains.compose.web.testutils.* import org.jetbrains.compose.web.testutils.*
import org.jetbrains.compose.web.dom.* import org.jetbrains.compose.web.dom.*
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
@ -113,8 +112,8 @@ class ElementsTests {
fun rawCreation() = runTest { fun rawCreation() = runTest {
@Composable @Composable
fun CustomElement( fun CustomElement(
attrs: AttrsBuilder<HTMLElement>.() -> Unit, attrs: AttrsScope<HTMLElement>.() -> Unit,
content: ContentBuilder<HTMLElement>? = null content: ContentBuilder<HTMLElement>? = null
) { ) {
TagElement( TagElement(
tagName = "custom", tagName = "custom",

2
web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/tests/InputsTests.kt

@ -343,7 +343,7 @@ class InputsTests {
}) })
Div(attrs = { Div(attrs = {
addEventListener(EventsListenerBuilder.INPUT) { addEventListener(EventsListenerScope.INPUT) {
state2 = "div caught an input" state2 = "div caught an input"
} }
}) { }) {

258
web/svg/src/jsMain/kotlin/org/jetbrains/compose/web/svg/svg.kt

@ -61,9 +61,9 @@ private val View = ElementBuilderNS<SVGViewElement>("view", SVG_NS)
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun Svg( fun Svg(
viewBox: String? = null, viewBox: String? = null,
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Svg, elementBuilder = Svg,
@ -78,9 +78,9 @@ fun Svg(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.SvgA( fun ElementScope<SVGElement>.SvgA(
href: String, href: String,
attrs: AttrBuilderContext<SVGAElement>? = null, attrs: AttrsBuilder<SVGAElement>? = null,
content: ContentBuilder<SVGAElement>? = null content: ContentBuilder<SVGAElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = A, elementBuilder = A,
@ -95,11 +95,11 @@ fun ElementScope<SVGElement>.SvgA(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Circle( fun ElementScope<SVGElement>.Circle(
cx: CSSLengthOrPercentageValue, cx: CSSLengthOrPercentageValue,
cy: CSSLengthOrPercentageValue, cy: CSSLengthOrPercentageValue,
r: CSSLengthOrPercentageValue, r: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGCircleElement>? = null, attrs: AttrsBuilder<SVGCircleElement>? = null,
content: ContentBuilder<SVGCircleElement>? = null content: ContentBuilder<SVGCircleElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Circle, elementBuilder = Circle,
@ -117,11 +117,11 @@ fun ElementScope<SVGElement>.Circle(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Circle( fun ElementScope<SVGElement>.Circle(
cx: Number, cx: Number,
cy: Number, cy: Number,
r: Number, r: Number,
attrs: AttrBuilderContext<SVGCircleElement>? = null, attrs: AttrsBuilder<SVGCircleElement>? = null,
content: ContentBuilder<SVGCircleElement>? = null content: ContentBuilder<SVGCircleElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Circle, elementBuilder = Circle,
@ -139,10 +139,10 @@ fun ElementScope<SVGElement>.Circle(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.SvgText( fun ElementScope<SVGElement>.SvgText(
text: String, text: String,
x: Number = 0, x: Number = 0,
y: Number = 0, y: Number = 0,
attrs: AttrBuilderContext<SVGTextElement>? = null, attrs: AttrsBuilder<SVGTextElement>? = null,
) { ) {
TagElement( TagElement(
elementBuilder = Text, elementBuilder = Text,
@ -160,9 +160,9 @@ fun ElementScope<SVGElement>.SvgText(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.View( fun ElementScope<SVGElement>.View(
id: String, id: String,
viewBox: String, viewBox: String,
attrs: AttrBuilderContext<SVGViewElement>? = null, attrs: AttrsBuilder<SVGViewElement>? = null,
) { ) {
TagElement( TagElement(
elementBuilder = View, elementBuilder = View,
@ -178,12 +178,12 @@ fun ElementScope<SVGElement>.View(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Rect( fun ElementScope<SVGElement>.Rect(
x: Number, x: Number,
y: Number, y: Number,
width: Number, width: Number,
height: Number, height: Number,
attrs: AttrBuilderContext<SVGRectElement>? = null, attrs: AttrsBuilder<SVGRectElement>? = null,
content: ContentBuilder<SVGRectElement>? = null content: ContentBuilder<SVGRectElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Rect, elementBuilder = Rect,
@ -201,12 +201,12 @@ fun ElementScope<SVGElement>.Rect(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Rect( fun ElementScope<SVGElement>.Rect(
x: CSSLengthOrPercentageValue, x: CSSLengthOrPercentageValue,
y: CSSLengthOrPercentageValue, y: CSSLengthOrPercentageValue,
width: CSSLengthOrPercentageValue, width: CSSLengthOrPercentageValue,
height: CSSLengthOrPercentageValue, height: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGRectElement>? = null, attrs: AttrsBuilder<SVGRectElement>? = null,
content: ContentBuilder<SVGRectElement>? = null content: ContentBuilder<SVGRectElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Rect, elementBuilder = Rect,
@ -224,12 +224,12 @@ fun ElementScope<SVGElement>.Rect(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Ellipse( fun ElementScope<SVGElement>.Ellipse(
cx: CSSLengthOrPercentageValue, cx: CSSLengthOrPercentageValue,
cy: CSSLengthOrPercentageValue, cy: CSSLengthOrPercentageValue,
rx: CSSLengthOrPercentageValue, rx: CSSLengthOrPercentageValue,
ry: CSSLengthOrPercentageValue, ry: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGEllipseElement>? = null, attrs: AttrsBuilder<SVGEllipseElement>? = null,
content: ContentBuilder<SVGEllipseElement>? = null content: ContentBuilder<SVGEllipseElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Ellipse, elementBuilder = Ellipse,
@ -247,12 +247,12 @@ fun ElementScope<SVGElement>.Ellipse(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Ellipse( fun ElementScope<SVGElement>.Ellipse(
cx: Number, cx: Number,
cy: Number, cy: Number,
rx: Number, rx: Number,
ry: Number, ry: Number,
attrs: AttrBuilderContext<SVGEllipseElement>? = null, attrs: AttrsBuilder<SVGEllipseElement>? = null,
content: ContentBuilder<SVGEllipseElement>? = null content: ContentBuilder<SVGEllipseElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Ellipse, elementBuilder = Ellipse,
@ -271,9 +271,9 @@ fun ElementScope<SVGElement>.Ellipse(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Symbol( fun ElementScope<SVGElement>.Symbol(
id: String? = null, id: String? = null,
attrs: AttrBuilderContext<SVGSymbolElement>? = null, attrs: AttrsBuilder<SVGSymbolElement>? = null,
content: ContentBuilder<SVGSymbolElement>? = null content: ContentBuilder<SVGSymbolElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Symbol, elementBuilder = Symbol,
@ -288,9 +288,9 @@ fun ElementScope<SVGElement>.Symbol(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Use( fun ElementScope<SVGElement>.Use(
href: String, href: String,
attrs: AttrBuilderContext<SVGUseElement>? = null, attrs: AttrsBuilder<SVGUseElement>? = null,
content: ContentBuilder<SVGUseElement>? = null content: ContentBuilder<SVGUseElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Use, elementBuilder = Use,
@ -305,12 +305,12 @@ fun ElementScope<SVGElement>.Use(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Line( fun ElementScope<SVGElement>.Line(
x1: CSSLengthOrPercentageValue, x1: CSSLengthOrPercentageValue,
y1: CSSLengthOrPercentageValue, y1: CSSLengthOrPercentageValue,
x2: CSSLengthOrPercentageValue, x2: CSSLengthOrPercentageValue,
y2: CSSLengthOrPercentageValue, y2: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGLineElement>? = null, attrs: AttrsBuilder<SVGLineElement>? = null,
content: ContentBuilder<SVGLineElement>? = null content: ContentBuilder<SVGLineElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Line, elementBuilder = Line,
@ -328,12 +328,12 @@ fun ElementScope<SVGElement>.Line(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Line( fun ElementScope<SVGElement>.Line(
x1: Number, x1: Number,
y1: Number, y1: Number,
x2: Number, x2: Number,
y2: Number, y2: Number,
attrs: AttrBuilderContext<SVGLineElement>? = null, attrs: AttrsBuilder<SVGLineElement>? = null,
content: ContentBuilder<SVGLineElement>? = null content: ContentBuilder<SVGLineElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Line, elementBuilder = Line,
@ -352,9 +352,9 @@ fun ElementScope<SVGElement>.Line(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.ClipPath( fun ElementScope<SVGElement>.ClipPath(
id: String, id: String,
attrs: AttrBuilderContext<SVGClipPathElement>? = null, attrs: AttrsBuilder<SVGClipPathElement>? = null,
content: ContentBuilder<SVGClipPathElement>? = null content: ContentBuilder<SVGClipPathElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = ClipPath, elementBuilder = ClipPath,
@ -369,9 +369,9 @@ fun ElementScope<SVGElement>.ClipPath(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Path( fun ElementScope<SVGElement>.Path(
d: String, d: String,
attrs: AttrBuilderContext<SVGPathElement>? = null, attrs: AttrsBuilder<SVGPathElement>? = null,
content: ContentBuilder<SVGPathElement>? = null content: ContentBuilder<SVGPathElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Path, elementBuilder = Path,
@ -386,8 +386,8 @@ fun ElementScope<SVGElement>.Path(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.G( fun ElementScope<SVGElement>.G(
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = G, elementBuilder = G,
@ -399,9 +399,9 @@ fun ElementScope<SVGElement>.G(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Image( fun ElementScope<SVGElement>.Image(
href: String, href: String,
attrs: AttrBuilderContext<SVGImageElement>? = null, attrs: AttrsBuilder<SVGImageElement>? = null,
content: ContentBuilder<SVGImageElement>? = null content: ContentBuilder<SVGImageElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Image, elementBuilder = Image,
@ -416,9 +416,9 @@ fun ElementScope<SVGElement>.Image(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Mask( fun ElementScope<SVGElement>.Mask(
id: String? = null, id: String? = null,
attrs: AttrBuilderContext<SVGMaskElement>? = null, attrs: AttrsBuilder<SVGMaskElement>? = null,
content: ContentBuilder<SVGMaskElement>? = null content: ContentBuilder<SVGMaskElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Mask, elementBuilder = Mask,
@ -433,8 +433,8 @@ fun ElementScope<SVGElement>.Mask(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Defs( fun ElementScope<SVGElement>.Defs(
attrs: AttrBuilderContext<SVGDefsElement>? = null, attrs: AttrsBuilder<SVGDefsElement>? = null,
content: ContentBuilder<SVGDefsElement>? = null content: ContentBuilder<SVGDefsElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Defs, elementBuilder = Defs,
@ -446,9 +446,9 @@ fun ElementScope<SVGElement>.Defs(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Pattern( fun ElementScope<SVGElement>.Pattern(
id: String, id: String,
attrs: AttrBuilderContext<SVGPatternElement>? = null, attrs: AttrsBuilder<SVGPatternElement>? = null,
content: ContentBuilder<SVGPatternElement>? = null content: ContentBuilder<SVGPatternElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Pattern, elementBuilder = Pattern,
@ -463,9 +463,9 @@ fun ElementScope<SVGElement>.Pattern(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Polygon( fun ElementScope<SVGElement>.Polygon(
vararg points: Number, vararg points: Number,
attrs: AttrBuilderContext<SVGPolygonElement>? = null, attrs: AttrsBuilder<SVGPolygonElement>? = null,
content: ContentBuilder<SVGPolygonElement>? = null content: ContentBuilder<SVGPolygonElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Polygon, elementBuilder = Polygon,
@ -480,9 +480,9 @@ fun ElementScope<SVGElement>.Polygon(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Polyline( fun ElementScope<SVGElement>.Polyline(
vararg points: Number, vararg points: Number,
attrs: AttrBuilderContext<SVGPolylineElement>? = null, attrs: AttrsBuilder<SVGPolylineElement>? = null,
content: ContentBuilder<SVGPolylineElement>? = null content: ContentBuilder<SVGPolylineElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Polyline, elementBuilder = Polyline,
@ -497,9 +497,9 @@ fun ElementScope<SVGElement>.Polyline(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.TextPath( fun ElementScope<SVGElement>.TextPath(
href: String, href: String,
text: String, text: String,
attrs: AttrBuilderContext<SVGTextPathElement>? = null, attrs: AttrsBuilder<SVGTextPathElement>? = null,
) { ) {
TagElement( TagElement(
elementBuilder = TextPath, elementBuilder = TextPath,
@ -516,8 +516,8 @@ fun ElementScope<SVGElement>.TextPath(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Animate( fun ElementScope<SVGElement>.Animate(
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Animate, elementBuilder = Animate,
@ -529,8 +529,8 @@ fun ElementScope<SVGElement>.Animate(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.AnimateMotion( fun ElementScope<SVGElement>.AnimateMotion(
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = AnimateMotion, elementBuilder = AnimateMotion,
@ -542,8 +542,8 @@ fun ElementScope<SVGElement>.AnimateMotion(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.AnimateTransform( fun ElementScope<SVGElement>.AnimateTransform(
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = AnimateTransform, elementBuilder = AnimateTransform,
@ -555,9 +555,9 @@ fun ElementScope<SVGElement>.AnimateTransform(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.LinearGradient( fun ElementScope<SVGElement>.LinearGradient(
id: String? = null, id: String? = null,
attrs: AttrBuilderContext<SVGLinearGradientElement>? = null, attrs: AttrsBuilder<SVGLinearGradientElement>? = null,
content: ContentBuilder<SVGLinearGradientElement>? = null content: ContentBuilder<SVGLinearGradientElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = LinearGradient, elementBuilder = LinearGradient,
@ -573,9 +573,9 @@ fun ElementScope<SVGElement>.LinearGradient(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.RadialGradient( fun ElementScope<SVGElement>.RadialGradient(
id: String? = null, id: String? = null,
attrs: AttrBuilderContext<SVGRadialGradientElement>? = null, attrs: AttrsBuilder<SVGRadialGradientElement>? = null,
content: ContentBuilder<SVGRadialGradientElement>? = null content: ContentBuilder<SVGRadialGradientElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = RadialGradient, elementBuilder = RadialGradient,
@ -590,8 +590,8 @@ fun ElementScope<SVGElement>.RadialGradient(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Stop( fun ElementScope<SVGElement>.Stop(
attrs: AttrBuilderContext<SVGStopElement>? = null, attrs: AttrsBuilder<SVGStopElement>? = null,
content: ContentBuilder<SVGStopElement>? = null content: ContentBuilder<SVGStopElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Stop, elementBuilder = Stop,
@ -603,8 +603,8 @@ fun ElementScope<SVGElement>.Stop(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Switch( fun ElementScope<SVGElement>.Switch(
attrs: AttrBuilderContext<SVGSwitchElement>? = null, attrs: AttrsBuilder<SVGSwitchElement>? = null,
content: ContentBuilder<SVGSwitchElement>? = null content: ContentBuilder<SVGSwitchElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Switch, elementBuilder = Switch,
@ -616,8 +616,8 @@ fun ElementScope<SVGElement>.Switch(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Title( fun ElementScope<SVGElement>.Title(
text: String, text: String,
attrs: AttrBuilderContext<SVGTitleElement>? = null, attrs: AttrsBuilder<SVGTitleElement>? = null,
) { ) {
TagElement( TagElement(
elementBuilder = Title, elementBuilder = Title,
@ -631,8 +631,8 @@ fun ElementScope<SVGElement>.Title(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Tspan( fun ElementScope<SVGElement>.Tspan(
attrs: AttrBuilderContext<SVGTSpanElement>? = null, attrs: AttrsBuilder<SVGTSpanElement>? = null,
content: ContentBuilder<SVGTSpanElement>? = null content: ContentBuilder<SVGTSpanElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Tspan, elementBuilder = Tspan,
@ -644,8 +644,8 @@ fun ElementScope<SVGElement>.Tspan(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Desc( fun ElementScope<SVGElement>.Desc(
content: String, content: String,
attrs: AttrBuilderContext<SVGDescElement>? = null, attrs: AttrsBuilder<SVGDescElement>? = null,
) { ) {
TagElement( TagElement(
elementBuilder = Desc, elementBuilder = Desc,
@ -659,8 +659,8 @@ fun ElementScope<SVGElement>.Desc(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Marker( fun ElementScope<SVGElement>.Marker(
attrs: AttrBuilderContext<SVGMarkerElement>? = null, attrs: AttrsBuilder<SVGMarkerElement>? = null,
content: ContentBuilder<SVGMarkerElement>? = null content: ContentBuilder<SVGMarkerElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Marker, elementBuilder = Marker,
@ -672,8 +672,8 @@ fun ElementScope<SVGElement>.Marker(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Mpath( fun ElementScope<SVGElement>.Mpath(
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Mpath, elementBuilder = Mpath,
@ -685,8 +685,8 @@ fun ElementScope<SVGElement>.Mpath(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Filter( fun ElementScope<SVGElement>.Filter(
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Filter, elementBuilder = Filter,
@ -698,10 +698,10 @@ fun ElementScope<SVGElement>.Filter(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Set( fun ElementScope<SVGElement>.Set(
attributeName: String, attributeName: String,
to: String, to: String,
attrs: AttrBuilderContext<SVGElement>? = null, attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null content: ContentBuilder<SVGElement>? = null
) { ) {
TagElement( TagElement(
elementBuilder = Set, elementBuilder = Set,
@ -717,13 +717,13 @@ fun ElementScope<SVGElement>.Set(
@Composable @Composable
@ExperimentalComposeWebSvgApi @ExperimentalComposeWebSvgApi
fun <T : SVGElement> SvgElement( fun <T : SVGElement> SvgElement(
name: String, name: String,
attrs: AttrBuilderContext<T>? = null, attrs: AttrsBuilder<T>? = null,
content: ContentBuilder<T>? = null content: ContentBuilder<T>? = null
) { ) {
TagElement( TagElement(
elementBuilder = ElementBuilderNS(name, SVG_NS), elementBuilder = ElementBuilderNS(name, SVG_NS),
applyAttrs = attrs, applyAttrs = attrs,
content = content content = content
) )
} }

8
web/widgets/src/jsMain/kotlin/Modifier.kt

@ -8,7 +8,7 @@ import org.jetbrains.compose.web.css.margin
import org.jetbrains.compose.web.css.px import org.jetbrains.compose.web.css.px
import org.jetbrains.compose.web.css.Color.RGB import org.jetbrains.compose.web.css.Color.RGB
import org.jetbrains.compose.common.internal.castOrCreate import org.jetbrains.compose.common.internal.castOrCreate
import org.jetbrains.compose.web.attributes.AttrsBuilder import org.jetbrains.compose.web.attributes.AttrsScope
@ExperimentalComposeWebWidgetsApi @ExperimentalComposeWebWidgetsApi
@Deprecated(message = webWidgetsDeprecationMessage) @Deprecated(message = webWidgetsDeprecationMessage)
@ -21,10 +21,10 @@ actual fun Modifier.background(color: Color): Modifier = castOrCreate().apply {
@ExperimentalComposeWebWidgetsApi @ExperimentalComposeWebWidgetsApi
@Deprecated(message = webWidgetsDeprecationMessage) @Deprecated(message = webWidgetsDeprecationMessage)
fun Modifier.asAttributeBuilderApplier( fun Modifier.asAttributeBuilderApplier(
passThroughHandler: (AttrsBuilder<*>.() -> Unit)? = null passThroughHandler: (AttrsScope<*>.() -> Unit)? = null
): AttrsBuilder<*>.() -> Unit = ): AttrsScope<*>.() -> Unit =
castOrCreate().let { modifier -> castOrCreate().let { modifier ->
val st: AttrsBuilder<*>.() -> Unit = { val st: AttrsScope<*>.() -> Unit = {
modifier.attrHandlers.forEach { it.invoke(this) } modifier.attrHandlers.forEach { it.invoke(this) }
style { style {

6
web/widgets/src/jsMain/kotlin/internal/ActualModifier.kt

@ -4,19 +4,19 @@ import org.jetbrains.compose.annotations.webWidgetsDeprecationMessage
import org.jetbrains.compose.common.ui.ExperimentalComposeWebWidgetsApi import org.jetbrains.compose.common.ui.ExperimentalComposeWebWidgetsApi
import org.jetbrains.compose.common.ui.Modifier import org.jetbrains.compose.common.ui.Modifier
import org.jetbrains.compose.web.css.StyleBuilder import org.jetbrains.compose.web.css.StyleBuilder
import org.jetbrains.compose.web.attributes.AttrsBuilder import org.jetbrains.compose.web.attributes.AttrsScope
@ExperimentalComposeWebWidgetsApi @ExperimentalComposeWebWidgetsApi
@Deprecated(message = webWidgetsDeprecationMessage) @Deprecated(message = webWidgetsDeprecationMessage)
class ActualModifier : Modifier { class ActualModifier : Modifier {
val styleHandlers = mutableListOf<StyleBuilder.() -> Unit>() val styleHandlers = mutableListOf<StyleBuilder.() -> Unit>()
val attrHandlers = mutableListOf<AttrsBuilder<*>.() -> Unit>() val attrHandlers = mutableListOf<AttrsScope<*>.() -> Unit>()
fun add(builder: StyleBuilder.() -> Unit) { fun add(builder: StyleBuilder.() -> Unit) {
styleHandlers.add(builder) styleHandlers.add(builder)
} }
fun addAttributeBuilder(builder: AttrsBuilder<*>.() -> Unit) { fun addAttributeBuilder(builder: AttrsScope<*>.() -> Unit) {
attrHandlers.add(builder) attrHandlers.add(builder)
} }
} }

Loading…
Cancel
Save