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 2 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/
/captures
.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 {
js(IR) {
browser() {
browser {
testTask {
useKarma {
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.jetbrains.compose.gradle.kotlinKarmaConfig
@ -19,7 +18,7 @@ fun Project.isSampleProject() = projectDir.parentFile.name == "examples"
tasks.register("printBundleSize") {
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"
version = COMPOSE_WEB_VERSION
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>() {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions.jvmTarget = "11"
}
@ -54,6 +53,16 @@ subprojects {
}
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 {
dependsOn(tasks.named("jsTest"))
doLast {
@ -72,7 +81,6 @@ subprojects {
}
}
if (isSampleProject()) {
val printBundleSize by tasks.registering {
dependsOn(tasks.named("jsBrowserDistribution"))
@ -93,10 +101,10 @@ subprojects {
configurations.all {
resolutionStrategy.dependencySubstitution {
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 {
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.HTMLTextAreaElement
fun AttrsBuilder<HTMLAnchorElement>.href(value: String) =
fun AttrsScope<HTMLAnchorElement>.href(value: String) =
attr("href", value)
fun AttrsBuilder<HTMLAnchorElement>.target(value: ATarget = ATarget.Self) =
fun AttrsScope<HTMLAnchorElement>.target(value: ATarget = ATarget.Self) =
attr("target", value.targetStr)
fun AttrsBuilder<HTMLAnchorElement>.ref(value: ARel) =
fun AttrsScope<HTMLAnchorElement>.ref(value: ARel) =
attr("rel", value.relStr)
fun AttrsBuilder<HTMLAnchorElement>.ping(value: String) =
fun AttrsScope<HTMLAnchorElement>.ping(value: String) =
attr("ping", value)
fun AttrsBuilder<HTMLAnchorElement>.ping(vararg urls: String) =
fun AttrsScope<HTMLAnchorElement>.ping(vararg urls: String) =
attr("ping", urls.joinToString(" "))
fun AttrsBuilder<HTMLAnchorElement>.hreflang(value: String) =
fun AttrsScope<HTMLAnchorElement>.hreflang(value: String) =
attr("hreflang", value)
fun AttrsBuilder<HTMLAnchorElement>.download(value: String = "") =
fun AttrsScope<HTMLAnchorElement>.download(value: String = "") =
attr("download", value)
/* Button attributes */
fun AttrsBuilder<HTMLButtonElement>.autoFocus() =
fun AttrsScope<HTMLButtonElement>.autoFocus() =
attr("autofocus", "")
fun AttrsBuilder<HTMLButtonElement>.disabled() =
fun AttrsScope<HTMLButtonElement>.disabled() =
attr("disabled", "")
fun AttrsBuilder<HTMLButtonElement>.form(formId: String) =
fun AttrsScope<HTMLButtonElement>.form(formId: String) =
attr("form", formId)
fun AttrsBuilder<HTMLButtonElement>.formAction(url: String) =
fun AttrsScope<HTMLButtonElement>.formAction(url: String) =
attr("formaction", url)
fun AttrsBuilder<HTMLButtonElement>.formEncType(value: ButtonFormEncType) =
fun AttrsScope<HTMLButtonElement>.formEncType(value: ButtonFormEncType) =
attr("formenctype", value.typeStr)
fun AttrsBuilder<HTMLButtonElement>.formMethod(value: ButtonFormMethod) =
fun AttrsScope<HTMLButtonElement>.formMethod(value: ButtonFormMethod) =
attr("formmethod", value.methodStr)
fun AttrsBuilder<HTMLButtonElement>.formNoValidate() =
fun AttrsScope<HTMLButtonElement>.formNoValidate() =
attr("formnovalidate", "")
fun AttrsBuilder<HTMLButtonElement>.formTarget(value: ButtonFormTarget) =
fun AttrsScope<HTMLButtonElement>.formTarget(value: ButtonFormTarget) =
attr("formtarget", value.targetStr)
fun AttrsBuilder<HTMLButtonElement>.name(value: String) =
fun AttrsScope<HTMLButtonElement>.name(value: String) =
attr("name", value)
fun AttrsBuilder<HTMLButtonElement>.type(value: ButtonType) =
fun AttrsScope<HTMLButtonElement>.type(value: ButtonType) =
attr("type", value.str)
fun AttrsBuilder<HTMLButtonElement>.value(value: String) =
fun AttrsScope<HTMLButtonElement>.value(value: String) =
attr("value", value)
/* Form attributes */
fun AttrsBuilder<HTMLFormElement>.action(value: String) =
fun AttrsScope<HTMLFormElement>.action(value: String) =
attr("action", value)
fun AttrsBuilder<HTMLFormElement>.acceptCharset(value: String) =
fun AttrsScope<HTMLFormElement>.acceptCharset(value: String) =
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")
fun AttrsBuilder<HTMLFormElement>.encType(value: FormEncType) =
fun AttrsScope<HTMLFormElement>.encType(value: FormEncType) =
attr("enctype", value.typeStr)
fun AttrsBuilder<HTMLFormElement>.method(value: FormMethod) =
fun AttrsScope<HTMLFormElement>.method(value: FormMethod) =
attr("method", value.methodStr)
fun AttrsBuilder<HTMLFormElement>.noValidate() =
fun AttrsScope<HTMLFormElement>.noValidate() =
attr("novalidate", "")
fun AttrsBuilder<HTMLFormElement>.target(value: FormTarget) =
fun AttrsScope<HTMLFormElement>.target(value: FormTarget) =
attr("target", value.targetStr)
fun AttrsBuilder<HTMLFormElement>.onSubmit(
fun AttrsScope<HTMLFormElement>.onSubmit(
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
) {
addEventListener(eventName = EventsListenerBuilder.RESET, listener = listener)
addEventListener(eventName = EventsListenerScope.RESET, listener = listener)
}
/* Input attributes */
fun AttrsBuilder<HTMLInputElement>.type(value: InputType<*>) =
fun AttrsScope<HTMLInputElement>.type(value: InputType<*>) =
attr("type", value.typeStr)
fun AttrsBuilder<HTMLInputElement>.accept(value: String) =
fun AttrsScope<HTMLInputElement>.accept(value: String) =
attr("accept", value) // type: file only
fun AttrsBuilder<HTMLInputElement>.alt(value: String) =
fun AttrsScope<HTMLInputElement>.alt(value: String) =
attr("alt", value) // type: image only
fun AttrsBuilder<HTMLInputElement>.autoComplete(value: AutoComplete) =
fun AttrsScope<HTMLInputElement>.autoComplete(value: AutoComplete) =
attr("autocomplete", value.unsafeCast<String>())
fun AttrsBuilder<HTMLInputElement>.autoFocus() =
fun AttrsScope<HTMLInputElement>.autoFocus() =
attr("autofocus", "")
fun AttrsBuilder<HTMLInputElement>.capture(value: String) =
fun AttrsScope<HTMLInputElement>.capture(value: String) =
attr("capture", value) // type: file only
fun AttrsBuilder<HTMLInputElement>.dirName(value: String) =
fun AttrsScope<HTMLInputElement>.dirName(value: String) =
attr("dirname", value) // text, search
fun AttrsBuilder<HTMLInputElement>.disabled() =
fun AttrsScope<HTMLInputElement>.disabled() =
attr("disabled", "")
fun AttrsBuilder<HTMLInputElement>.form(id: String) =
fun AttrsScope<HTMLInputElement>.form(id: String) =
attr("form", id)
fun AttrsBuilder<HTMLInputElement>.formAction(url: String) =
fun AttrsScope<HTMLInputElement>.formAction(url: String) =
attr("formaction", url)
fun AttrsBuilder<HTMLInputElement>.formEncType(value: InputFormEncType) =
fun AttrsScope<HTMLInputElement>.formEncType(value: InputFormEncType) =
attr("formenctype", value.typeStr)
fun AttrsBuilder<HTMLInputElement>.formMethod(value: InputFormMethod) =
fun AttrsScope<HTMLInputElement>.formMethod(value: InputFormMethod) =
attr("formmethod", value.methodStr)
fun AttrsBuilder<HTMLInputElement>.formNoValidate() =
fun AttrsScope<HTMLInputElement>.formNoValidate() =
attr("formnovalidate", "")
fun AttrsBuilder<HTMLInputElement>.formTarget(value: InputFormTarget) =
fun AttrsScope<HTMLInputElement>.formTarget(value: InputFormTarget) =
attr("formtarget", value.targetStr)
fun AttrsBuilder<HTMLInputElement>.height(value: Int) =
fun AttrsScope<HTMLInputElement>.height(value: Int) =
attr("height", value.toString()) // image only
fun AttrsBuilder<HTMLInputElement>.width(value: Int) =
fun AttrsScope<HTMLInputElement>.width(value: Int) =
attr("width", value.toString()) // image only
fun AttrsBuilder<HTMLInputElement>.list(dataListId: String) =
fun AttrsScope<HTMLInputElement>.list(dataListId: String) =
attr("list", dataListId)
fun AttrsBuilder<HTMLInputElement>.max(value: String) =
fun AttrsScope<HTMLInputElement>.max(value: String) =
attr("max", value)
fun AttrsBuilder<HTMLInputElement>.maxLength(value: Int) =
fun AttrsScope<HTMLInputElement>.maxLength(value: Int) =
attr("maxlength", value.toString())
fun AttrsBuilder<HTMLInputElement>.min(value: String) =
fun AttrsScope<HTMLInputElement>.min(value: String) =
attr("min", value)
fun AttrsBuilder<HTMLInputElement>.minLength(value: Int) =
fun AttrsScope<HTMLInputElement>.minLength(value: Int) =
attr("minlength", value.toString())
fun AttrsBuilder<HTMLInputElement>.multiple() =
fun AttrsScope<HTMLInputElement>.multiple() =
attr("multiple", "")
fun AttrsBuilder<HTMLInputElement>.name(value: String) =
fun AttrsScope<HTMLInputElement>.name(value: String) =
attr("name", value)
fun AttrsBuilder<HTMLInputElement>.pattern(value: String) =
fun AttrsScope<HTMLInputElement>.pattern(value: String) =
attr("pattern", value)
fun AttrsBuilder<HTMLInputElement>.placeholder(value: String) =
fun AttrsScope<HTMLInputElement>.placeholder(value: String) =
attr("placeholder", value)
fun AttrsBuilder<HTMLInputElement>.readOnly() =
fun AttrsScope<HTMLInputElement>.readOnly() =
attr("readonly", "")
fun AttrsBuilder<HTMLInputElement>.required(value: Boolean = true) =
fun AttrsScope<HTMLInputElement>.required(value: Boolean = true) =
attr("required", value.toString())
fun AttrsBuilder<HTMLInputElement>.size(value: Int) =
fun AttrsScope<HTMLInputElement>.size(value: Int) =
attr("size", value.toString())
fun AttrsBuilder<HTMLInputElement>.src(value: String) =
fun AttrsScope<HTMLInputElement>.src(value: String) =
attr("src", value) // image only
fun AttrsBuilder<HTMLInputElement>.step(value: Number) =
fun AttrsScope<HTMLInputElement>.step(value: Number) =
attr("step", value.toString()) // numeric types only
/* Option attributes */
fun AttrsBuilder<HTMLOptionElement>.value(value: String) =
fun AttrsScope<HTMLOptionElement>.value(value: String) =
attr("value", value)
fun AttrsBuilder<HTMLOptionElement>.disabled() =
fun AttrsScope<HTMLOptionElement>.disabled() =
attr("disabled", "")
fun AttrsBuilder<HTMLOptionElement>.selected() =
fun AttrsScope<HTMLOptionElement>.selected() =
attr("selected", "")
fun AttrsBuilder<HTMLOptionElement>.label(value: String) =
fun AttrsScope<HTMLOptionElement>.label(value: String) =
attr("label", value)
/* Select attributes */
fun AttrsBuilder<HTMLSelectElement>.autoComplete(value: AutoComplete) =
fun AttrsScope<HTMLSelectElement>.autoComplete(value: AutoComplete) =
attr("autocomplete", value.unsafeCast<String>())
fun AttrsBuilder<HTMLSelectElement>.autofocus() =
fun AttrsScope<HTMLSelectElement>.autofocus() =
attr("autofocus", "")
fun AttrsBuilder<HTMLSelectElement>.disabled() =
fun AttrsScope<HTMLSelectElement>.disabled() =
attr("disabled", "")
fun AttrsBuilder<HTMLSelectElement>.form(formId: String) =
fun AttrsScope<HTMLSelectElement>.form(formId: String) =
attr("form", formId)
fun AttrsBuilder<HTMLSelectElement>.multiple() =
fun AttrsScope<HTMLSelectElement>.multiple() =
attr("multiple", "")
fun AttrsBuilder<HTMLSelectElement>.name(value: String) =
fun AttrsScope<HTMLSelectElement>.name(value: String) =
attr("name", value)
fun AttrsBuilder<HTMLSelectElement>.required() =
fun AttrsScope<HTMLSelectElement>.required() =
attr("required", "")
fun AttrsBuilder<HTMLSelectElement>.size(numberOfRows: Int) =
fun AttrsScope<HTMLSelectElement>.size(numberOfRows: Int) =
attr("size", numberOfRows.toString())
/* OptGroup attributes */
fun AttrsBuilder<HTMLOptGroupElement>.label(value: String) =
fun AttrsScope<HTMLOptGroupElement>.label(value: String) =
attr("label", value)
fun AttrsBuilder<HTMLOptGroupElement>.disabled() =
fun AttrsScope<HTMLOptGroupElement>.disabled() =
attr("disabled", "")
/* TextArea attributes */
fun AttrsBuilder<HTMLTextAreaElement>.autoComplete(value: AutoComplete) =
fun AttrsScope<HTMLTextAreaElement>.autoComplete(value: AutoComplete) =
attr("autocomplete", value.unsafeCast<String>())
fun AttrsBuilder<HTMLTextAreaElement>.autoFocus() =
fun AttrsScope<HTMLTextAreaElement>.autoFocus() =
attr("autofocus", "")
fun AttrsBuilder<HTMLTextAreaElement>.cols(value: Int) =
fun AttrsScope<HTMLTextAreaElement>.cols(value: Int) =
attr("cols", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.disabled() =
fun AttrsScope<HTMLTextAreaElement>.disabled() =
attr("disabled", "")
fun AttrsBuilder<HTMLTextAreaElement>.form(formId: String) =
fun AttrsScope<HTMLTextAreaElement>.form(formId: String) =
attr("form", formId)
fun AttrsBuilder<HTMLTextAreaElement>.maxLength(value: Int) =
fun AttrsScope<HTMLTextAreaElement>.maxLength(value: Int) =
attr("maxlength", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.minLength(value: Int) =
fun AttrsScope<HTMLTextAreaElement>.minLength(value: Int) =
attr("minlength", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.name(value: String) =
fun AttrsScope<HTMLTextAreaElement>.name(value: String) =
attr("name", value)
fun AttrsBuilder<HTMLTextAreaElement>.placeholder(value: String) =
fun AttrsScope<HTMLTextAreaElement>.placeholder(value: String) =
attr("placeholder", value)
fun AttrsBuilder<HTMLTextAreaElement>.readOnly() =
fun AttrsScope<HTMLTextAreaElement>.readOnly() =
attr("readonly", "")
fun AttrsBuilder<HTMLTextAreaElement>.required() =
fun AttrsScope<HTMLTextAreaElement>.required() =
attr("required", "")
fun AttrsBuilder<HTMLTextAreaElement>.rows(value: Int) =
fun AttrsScope<HTMLTextAreaElement>.rows(value: Int) =
attr("rows", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.wrap(value: TextAreaWrap) =
fun AttrsScope<HTMLTextAreaElement>.wrap(value: TextAreaWrap) =
attr("wrap", value.str)
/* Img attributes */
fun AttrsBuilder<HTMLImageElement>.src(value: String): AttrsBuilder<HTMLImageElement> =
fun AttrsScope<HTMLImageElement>.src(value: String): AttrsScope<HTMLImageElement> =
attr("src", value)
fun AttrsBuilder<HTMLImageElement>.alt(value: String): AttrsBuilder<HTMLImageElement> =
fun AttrsScope<HTMLImageElement>.alt(value: String): AttrsScope<HTMLImageElement> =
attr("alt", value)
@ -313,19 +313,19 @@ internal val setCheckedValue: (HTMLInputElement, Boolean) -> Unit = { e, v ->
}
/* Img attributes */
fun AttrsBuilder<HTMLLabelElement>.forId(value: String): AttrsBuilder<HTMLLabelElement> =
fun AttrsScope<HTMLLabelElement>.forId(value: String): AttrsScope<HTMLLabelElement> =
attr("for", value)
/* Table attributes */
fun AttrsBuilder<HTMLTableColElement>.span(value: Int): AttrsBuilder<HTMLTableColElement> =
fun AttrsScope<HTMLTableColElement>.span(value: Int): AttrsScope<HTMLTableColElement> =
attr("span", value.toString())
fun AttrsBuilder<HTMLTableCellElement>.scope(value: Scope): AttrsBuilder<HTMLTableCellElement> =
fun AttrsScope<HTMLTableCellElement>.scope(value: Scope): AttrsScope<HTMLTableCellElement> =
attr("scope", value.str)
fun AttrsBuilder<HTMLTableCellElement>.colspan(value: Int): AttrsBuilder<HTMLTableCellElement> =
fun AttrsScope<HTMLTableCellElement>.colspan(value: Int): AttrsScope<HTMLTableCellElement> =
attr("colspan", value.toString())
fun AttrsBuilder<HTMLTableCellElement>.rowspan(value: Int): AttrsBuilder<HTMLTableCellElement> =
fun AttrsScope<HTMLTableCellElement>.rowspan(value: Int): AttrsScope<HTMLTableCellElement> =
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.DisposableEffectScope
import org.jetbrains.compose.web.css.StyleBuilder
import org.jetbrains.compose.web.css.StyleBuilderImpl
import org.jetbrains.compose.web.css.StyleScope
import org.jetbrains.compose.web.css.StyleScopeBuilder
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.w3c.dom.Element
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,
* 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]
* are extracted to a separate methods.
*
*/
open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
internal val attributesMap = mutableMapOf<String, String>()
internal val styleBuilder = StyleBuilderImpl()
internal val propertyUpdates = mutableListOf<Pair<(Element, Any) -> Unit, Any>>()
internal var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)? = null
internal var inputControlledValueSet = false
internal var inputDefaultValueSet = false
internal var inputControlledCheckedSet = false
internal var inputDefaultCheckedSet = false
interface AttrsScope<TElement : Element>: EventsListenerScope {
@ComposeWebInternalApi
val attributesMap: Map<String, String>
@ComposeWebInternalApi
val styleScope: StyleScope
@ComposeWebInternalApi
val propertyUpdates: List<Pair<(Element, Any) -> Unit, Any>>
@ComposeWebInternalApi
var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)?
/**
* [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:
* ```
@ -39,18 +39,18 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
* })
* ```
*/
fun style(builder: StyleBuilder.() -> Unit) {
styleBuilder.apply(builder)
fun style(builder: StyleScope.() -> Unit) {
styleScope.apply(builder)
}
/**
* [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.
* 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.
*/
fun classes(vararg classes: String) = prop(setClassList, classes)
fun classes(vararg classes: String)= prop(setClassList, classes)
fun id(value: String) = attr(ID, value)
fun hidden() = attr(HIDDEN, true.toString())
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 tabIndex(value: Int) = attr(TAB_INDEX, value.toString())
fun spellCheck(value: Boolean) = attr(SPELLCHECK, value.toString())
/**
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode
*/
fun inputMode(value: String) = attr("inputmode", value)
/**
* see https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/inputmode
*/
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.
* 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)
*/
fun ref(effect: DisposableEffectScope.(TElement) -> DisposableEffectResult) {
override fun ref(effect: DisposableEffectScope.(TElement) -> DisposableEffectResult) {
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.
*/
fun attr(attr: String, value: String): AttrsBuilder<TElement> {
override fun attr(attr: String, value: String): AttrsScope<TElement> {
attributesMap[attr] = value
return this
}
@ -114,35 +177,22 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
* ```
*/
@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>)
}
internal fun collect(): Map<String, String> {
return attributesMap
}
internal fun copyFrom(attrsBuilder: AttrsBuilder<TElement>) {
refEffect = attrsBuilder.refEffect
styleBuilder.copyFrom(attrsBuilder.styleBuilder)
attributesMap.putAll(attrsBuilder.attributesMap)
propertyUpdates.addAll(attrsBuilder.propertyUpdates)
copyListenersFrom(attrsBuilder)
}
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"
override fun copyFrom(attrsScope: AttrsScope<TElement>) {
refEffect = attrsScope.refEffect
styleScope.copyFrom(attrsScope.styleScope)
attributesMap.putAll(attrsScope.attributesMap)
propertyUpdates.addAll(attrsScope.propertyUpdates)
copyListenersFrom(attrsScope)
}
}

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.SyntheticWheelEvent
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
private typealias SyntheticMouseEventListener = (SyntheticMouseEvent) -> Unit
private typealias SyntheticWheelEventListener = (SyntheticWheelEvent) -> 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
* exist dedicated method. In case you need to support event that doesn't have such method,
* use [addEventListener]
*/
open class EventsListenerBuilder {
protected val listeners = mutableListOf<SyntheticEventListener<*>>()
interface EventsListenerScope {
@ComposeWebInternalApi
val listeners: List<SyntheticEventListener<*>>
@ComposeWebInternalApi
fun registerEventListener(listener: SyntheticEventListener<*>)
/* Mouse Events */
fun onContextMenu(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(CONTEXTMENU, listener))
registerEventListener(MouseEventListener(CONTEXTMENU, listener))
}
fun onClick(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(CLICK, listener))
registerEventListener(MouseEventListener(CLICK, listener))
}
fun onDoubleClick(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(DBLCLICK, listener))
registerEventListener(MouseEventListener(DBLCLICK, listener))
}
fun onMouseDown(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEDOWN, listener))
registerEventListener(MouseEventListener(MOUSEDOWN, listener))
}
fun onMouseUp(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEUP, listener))
registerEventListener(MouseEventListener(MOUSEUP, listener))
}
fun onMouseEnter(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEENTER, listener))
registerEventListener(MouseEventListener(MOUSEENTER, listener))
}
fun onMouseLeave(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSELEAVE, listener))
registerEventListener(MouseEventListener(MOUSELEAVE, listener))
}
fun onMouseMove(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEMOVE, listener))
registerEventListener(MouseEventListener(MOUSEMOVE, listener))
}
fun onMouseOut(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEOUT, listener))
registerEventListener(MouseEventListener(MOUSEOUT, listener))
}
fun onMouseOver(listener: SyntheticMouseEventListener) {
listeners.add(MouseEventListener(MOUSEOVER, listener))
registerEventListener(MouseEventListener(MOUSEOVER, listener))
}
fun onWheel(listener: SyntheticWheelEventListener) {
listeners.add(MouseWheelEventListener(WHEEL, listener))
registerEventListener(MouseWheelEventListener(WHEEL, listener))
}
/* Drag Events */
fun onDrag(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAG, listener))
registerEventListener(DragEventListener(DRAG, listener))
}
fun onDrop(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DROP, listener))
registerEventListener(DragEventListener(DROP, listener))
}
fun onDragStart(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGSTART, listener))
registerEventListener(DragEventListener(DRAGSTART, listener))
}
fun onDragEnd(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGEND, listener))
registerEventListener(DragEventListener(DRAGEND, listener))
}
fun onDragOver(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGOVER, listener))
registerEventListener(DragEventListener(DRAGOVER, listener))
}
fun onDragEnter(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGENTER, listener))
registerEventListener(DragEventListener(DRAGENTER, listener))
}
fun onDragLeave(listener: SyntheticDragEventListener) {
listeners.add(DragEventListener(DRAGLEAVE, listener))
registerEventListener(DragEventListener(DRAGLEAVE, listener))
}
/* End of Drag Events */
/* Clipboard Events */
fun onCopy(listener: (SyntheticClipboardEvent) -> Unit) {
listeners.add(ClipboardEventListener(COPY, listener))
registerEventListener(ClipboardEventListener(COPY, listener))
}
fun onCut(listener: (SyntheticClipboardEvent) -> Unit) {
listeners.add(ClipboardEventListener(CUT, listener))
registerEventListener(ClipboardEventListener(CUT, listener))
}
fun onPaste(listener: (SyntheticClipboardEvent) -> Unit) {
listeners.add(ClipboardEventListener(PASTE, listener))
registerEventListener(ClipboardEventListener(PASTE, listener))
}
/* End of Clipboard Events */
/* Keyboard Events */
fun onKeyDown(listener: (SyntheticKeyboardEvent) -> Unit) {
listeners.add(KeyboardEventListener(KEYDOWN, listener))
registerEventListener(KeyboardEventListener(KEYDOWN, listener))
}
fun onKeyUp(listener: (SyntheticKeyboardEvent) -> Unit) {
listeners.add(KeyboardEventListener(KEYUP, listener))
registerEventListener(KeyboardEventListener(KEYUP, listener))
}
/* End of Keyboard Events */
/* Focus Events */
fun onFocus(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(FOCUS, listener))
registerEventListener(FocusEventListener(FOCUS, listener))
}
fun onBlur(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(BLUR, listener))
registerEventListener(FocusEventListener(BLUR, listener))
}
fun onFocusIn(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(FOCUSIN, listener))
registerEventListener(FocusEventListener(FOCUSIN, listener))
}
fun onFocusOut(listener: (SyntheticFocusEvent) -> Unit) {
listeners.add(FocusEventListener(FOCUSOUT, listener))
registerEventListener(FocusEventListener(FOCUSOUT, listener))
}
/* End of Focus Events */
/* Touch Events */
fun onTouchCancel(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHCANCEL, listener))
registerEventListener(TouchEventListener(TOUCHCANCEL, listener))
}
fun onTouchMove(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHMOVE, listener))
registerEventListener(TouchEventListener(TOUCHMOVE, listener))
}
fun onTouchEnd(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHEND, listener))
registerEventListener(TouchEventListener(TOUCHEND, listener))
}
fun onTouchStart(listener: (SyntheticTouchEvent) -> Unit) {
listeners.add(TouchEventListener(TOUCHSTART, listener))
registerEventListener(TouchEventListener(TOUCHSTART, listener))
}
/* End of Touch Events */
/* Animation Events */
fun onAnimationEnd(listener: (SyntheticAnimationEvent) -> Unit) {
listeners.add(AnimationEventListener(ANIMATIONEND, listener))
registerEventListener(AnimationEventListener(ANIMATIONEND, listener))
}
fun onAnimationIteration(listener: (SyntheticAnimationEvent) -> Unit) {
listeners.add(AnimationEventListener(ANIMATIONITERATION, listener))
registerEventListener(AnimationEventListener(ANIMATIONITERATION, listener))
}
fun onAnimationStart(listener: (SyntheticAnimationEvent) -> Unit) {
listeners.add(AnimationEventListener(ANIMATIONSTART, listener))
registerEventListener(AnimationEventListener(ANIMATIONSTART, listener))
}
/* End of Animation Events */
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
* @param eventName - the name of the event
@ -200,33 +209,32 @@ open class EventsListenerBuilder {
eventName: String,
listener: (T) -> Unit
) {
listeners.add(SyntheticEventListener(eventName, listener))
registerEventListener(SyntheticEventListener(eventName, listener))
}
fun addEventListener(
eventName: String,
listener: (SyntheticEvent<EventTarget>) -> Unit
) {
listeners.add(SyntheticEventListener(eventName, listener))
registerEventListener(SyntheticEventListener(eventName, listener))
}
internal fun copyListenersFrom(from: EventsListenerBuilder) {
listeners.addAll(from.listeners)
}
@ComposeWebInternalApi
fun copyListenersFrom(from: EventsListenerScope)
companion object {
const val COPY = "copy"
const val CUT = "cut"
const val PASTE = "paste"
const val CONTEXTMENU = "contextmenu"
const val CLICK = "click"
const val DBLCLICK = "dblclick"
const val FOCUS = "focus"
const val BLUR = "blur"
const val FOCUSIN = "focusin"
const val FOCUSOUT = "focusout"
const val KEYDOWN = "keydown"
const val KEYUP = "keyup"
const val MOUSEDOWN = "mousedown"
@ -239,22 +247,22 @@ open class EventsListenerBuilder {
const val WHEEL = "wheel"
const val SCROLL = "scroll"
const val SELECT = "select"
const val TOUCHCANCEL = "touchcancel"
const val TOUCHEND = "touchend"
const val TOUCHMOVE = "touchmove"
const val TOUCHSTART = "touchstart"
const val ANIMATIONCANCEL = "animationcancel" // firefox and safari only
const val ANIMATIONEND = "animationend"
const val ANIMATIONITERATION = "animationiteration"
const val ANIMATIONSTART = "animationstart"
const val BEFOREINPUT = "beforeinput"
const val INPUT = "input"
const val CHANGE = "change"
const val INVALID = "invalid"
const val DRAG = "drag"
const val DROP = "drop"
const val DRAGSTART = "dragstart"
@ -262,8 +270,20 @@ open class EventsListenerBuilder {
const val DRAGOVER = "dragover"
const val DRAGENTER = "dragenter"
const val DRAGLEAVE = "dragleave"
const val SUBMIT = "submit"
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.SyntheticMouseEvent
import androidx.compose.web.events.SyntheticWheelEvent
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.CHANGE
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.INPUT
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.SELECT
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.CHANGE
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.INPUT
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.SELECT
import org.jetbrains.compose.web.events.*
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
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 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.SyntheticInputEvent
import org.jetbrains.compose.web.events.SyntheticSelectEvent
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:
*
* [value] - sets the current input's value.
@ -28,11 +37,11 @@ import org.w3c.dom.HTMLInputElement
* [onBeforeInput] - add `beforeinput` event listener
* [onSelect] - add `select` event listener
*/
class InputAttrsBuilder<ValueType>(
class InputAttrsScope<ValueType>(
val inputType: InputType<ValueType>
) : AttrsBuilder<HTMLInputElement>() {
) : AttrsScopeBuilder<HTMLInputElement>() {
fun value(value: String): InputAttrsBuilder<ValueType> {
fun value(value: String): InputAttrsScope<ValueType> {
when (inputType) {
InputType.Checkbox,
InputType.Radio,
@ -43,27 +52,27 @@ class InputAttrsBuilder<ValueType>(
return this
}
fun value(value: Number): InputAttrsBuilder<ValueType> {
fun value(value: Number): InputAttrsScope<ValueType> {
value(value.toString())
return this
}
fun checked(checked: Boolean): InputAttrsBuilder<ValueType> {
fun checked(checked: Boolean): InputAttrsScope<ValueType> {
prop(setCheckedValue, checked)
return this
}
fun defaultChecked(): InputAttrsBuilder<ValueType> {
fun defaultChecked(): InputAttrsScope<ValueType> {
attr("checked", "")
return this
}
fun defaultValue(value: String): InputAttrsBuilder<ValueType> {
fun defaultValue(value: String): InputAttrsScope<ValueType> {
attr("value", value)
return this
}
fun defaultValue(value: Number): InputAttrsBuilder<ValueType> {
fun defaultValue(value: Number): InputAttrsScope<ValueType> {
attr("value", value.toString())
return this
}
@ -77,25 +86,25 @@ class InputAttrsBuilder<ValueType>(
fun onInput(
listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit
) {
listeners.add(InputEventListener(eventName = INPUT, inputType, listener))
registerEventListener(InputEventListener(eventName = INPUT, inputType, listener))
}
fun onChange(
listener: (SyntheticChangeEvent<ValueType, HTMLInputElement>) -> Unit
) {
listeners.add(ChangeEventListener(inputType, listener))
registerEventListener(ChangeEventListener(inputType, listener))
}
fun onBeforeInput(
listener: (SyntheticInputEvent<ValueType, HTMLInputElement>) -> Unit
) {
listeners.add(InputEventListener(eventName = BEFOREINPUT, inputType, listener))
registerEventListener(InputEventListener(eventName = BEFOREINPUT, inputType, listener))
}
fun onSelect(
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 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
import org.jetbrains.compose.web.attributes.AttrsBuilder
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.CHANGE
import org.jetbrains.compose.web.attributes.EventsListenerBuilder.Companion.INPUT
import org.jetbrains.compose.web.attributes.AttrsScopeBuilder
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.CHANGE
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.INPUT
import org.jetbrains.compose.web.attributes.SyntheticEventListener
import org.jetbrains.compose.web.events.SyntheticChangeEvent
import org.jetbrains.compose.web.events.SyntheticInputEvent
import org.w3c.dom.HTMLSelectElement
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(
listener: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit
) {
listeners.add(SelectInputEventListener(INPUT, listener))
registerEventListener(SelectInputEventListener(INPUT, listener))
}
fun onChange(
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
open class CSSRuleBuilderImpl : CSSStyleRuleBuilder, StyleBuilderImpl()
open class CSSRuleBuilderImpl : CSSStyleRuleBuilder, StyleScopeBuilder()
@Suppress("EqualsOrHashCode")
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
/**
* 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:
* 1. Add inlined css properties to the element (@see [property])
* 2. Set values to CSS variables (@see [variable])
@ -125,13 +125,26 @@ fun <TValue : StylePropertyValue> variable() =
}
interface StyleHolder {
@ComposeWebInternalApi
val properties: StylePropertyList
@ComposeWebInternalApi
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)
@Suppress("EqualsOrHashCode")
open class StyleBuilderImpl : StyleBuilder, StyleHolder {
open class StyleScopeBuilder : StyleScope {
override val properties: MutableStylePropertyList = mutableListOf()
override val variables: MutableStylePropertyList = mutableListOf()
@ -147,11 +160,11 @@ open class StyleBuilderImpl : StyleBuilder, StyleHolder {
override fun equals(other: Any?): Boolean {
return if (other is StyleHolder) {
properties.nativeEquals(other.properties) &&
variables.nativeEquals(other.variables)
variables.nativeEquals(other.variables)
} else false
}
internal fun copyFrom(sb: StyleBuilderImpl) {
override fun copyFrom(sb: StyleScope) {
properties.addAll(sb.properties)
variables.addAll(sb.variables)
}
@ -174,6 +187,6 @@ internal fun StylePropertyList.nativeEquals(properties: StylePropertyList): Bool
return all { prop ->
val otherProp = properties[index++]
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
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.attributes.AttrsScopeBuilder
import org.jetbrains.compose.web.css.StyleHolder
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.jetbrains.compose.web.internal.runtime.DomNodeWrapper
@ -95,7 +96,7 @@ private class DomElementWrapper(override val node: Element): DomNodeWrapper(node
@Composable
fun <TElement : Element> TagElement(
elementBuilder: ElementBuilder<TElement>,
applyAttrs: (AttrsBuilder<TElement>.() -> Unit)?,
applyAttrs: (AttrsScope<TElement>.() -> Unit)?,
content: (@Composable ElementScope<TElement>.() -> Unit)?
) {
val scope = remember { ElementScopeImpl<TElement>() }
@ -108,16 +109,16 @@ fun <TElement : Element> TagElement(
DomElementWrapper(node)
},
attrsSkippableUpdate = {
val attrsBuilder = AttrsBuilder<TElement>()
applyAttrs?.invoke(attrsBuilder)
val attrsScope = AttrsScopeBuilder<TElement>()
applyAttrs?.invoke(attrsScope)
refEffect = attrsBuilder.refEffect
refEffect = attrsScope.refEffect
update {
set(attrsBuilder.collect(), DomElementWrapper::updateAttrs)
set(attrsBuilder.collectListeners(), DomElementWrapper::updateEventListeners)
set(attrsBuilder.propertyUpdates, DomElementWrapper::updateProperties)
set(attrsBuilder.styleBuilder, DomElementWrapper::updateStyleDeclarations)
set(attrsScope.collect(), DomElementWrapper::updateAttrs)
set(attrsScope.collectListeners(), DomElementWrapper::updateEventListeners)
set(attrsScope.propertyUpdates, DomElementWrapper::updateProperties)
set(attrsScope.styleScope, DomElementWrapper::updateStyleDeclarations)
}
},
elementScope = scope,
@ -135,7 +136,7 @@ fun <TElement : Element> TagElement(
@ExperimentalComposeWebApi
fun <TElement : Element> TagElement(
tagName: String,
applyAttrs: AttrsBuilder<TElement>.() -> Unit,
applyAttrs: AttrsScope<TElement>.() -> Unit,
content: (@Composable ElementScope<TElement>.() -> Unit)?
) = TagElement(
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
import androidx.compose.runtime.*
import androidx.compose.web.attributes.SelectAttrsBuilder
import androidx.compose.web.attributes.SelectAttrsScope
import kotlinx.browser.document
import org.jetbrains.compose.web.attributes.*
import org.jetbrains.compose.web.attributes.builders.*
import org.jetbrains.compose.web.css.CSSRuleDeclarationList
import org.jetbrains.compose.web.css.StyleSheetBuilder
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.DomNodeWrapper
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import org.w3c.dom.Element
import org.w3c.dom.HTMLAnchorElement
import org.w3c.dom.HTMLAreaElement
@ -23,8 +23,8 @@ import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLEmbedElement
import org.w3c.dom.HTMLFieldSetElement
import org.w3c.dom.HTMLFormElement
import org.w3c.dom.HTMLHeadingElement
import org.w3c.dom.HTMLHRElement
import org.w3c.dom.HTMLHeadingElement
import org.w3c.dom.HTMLIFrameElement
import org.w3c.dom.HTMLImageElement
import org.w3c.dom.HTMLInputElement
@ -60,7 +60,12 @@ import org.w3c.dom.HTMLVideoElement
import org.w3c.dom.Text
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
private open class ElementBuilderImplementation<TElement : Element>(private val tagName: String) : ElementBuilder<TElement> {
@ -163,7 +168,7 @@ fun interface ElementBuilder<TElement : Element> {
@Composable
fun Address(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
@ -175,7 +180,7 @@ fun Address(
@Composable
fun Article(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
@ -187,7 +192,7 @@ fun Article(
@Composable
fun Aside(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
@ -199,7 +204,7 @@ fun Aside(
@Composable
fun Header(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
@ -211,7 +216,7 @@ fun Header(
@Composable
fun Area(
attrs: AttrBuilderContext<HTMLAreaElement>? = null,
attrs: AttrsBuilder<HTMLAreaElement>? = null,
content: ContentBuilder<HTMLAreaElement>? = null
) {
TagElement(
@ -223,7 +228,7 @@ fun Area(
@Composable
fun Audio(
attrs: AttrBuilderContext<HTMLAudioElement>? = null,
attrs: AttrsBuilder<HTMLAudioElement>? = null,
content: ContentBuilder<HTMLAudioElement>? = null
) {
TagElement(
@ -235,7 +240,7 @@ fun Audio(
@Composable
fun HTMLMap(
attrs: AttrBuilderContext<HTMLMapElement>? = null,
attrs: AttrsBuilder<HTMLMapElement>? = null,
content: ContentBuilder<HTMLMapElement>? = null
) {
TagElement(
@ -247,7 +252,7 @@ fun HTMLMap(
@Composable
fun Track(
attrs: AttrBuilderContext<HTMLTrackElement>? = null,
attrs: AttrsBuilder<HTMLTrackElement>? = null,
content: ContentBuilder<HTMLTrackElement>? = null
) {
TagElement(
@ -259,7 +264,7 @@ fun Track(
@Composable
fun Video(
attrs: AttrBuilderContext<HTMLVideoElement>? = null,
attrs: AttrsBuilder<HTMLVideoElement>? = null,
content: ContentBuilder<HTMLVideoElement>? = null
) {
TagElement(
@ -271,7 +276,7 @@ fun Video(
@Composable
fun Datalist(
attrs: AttrBuilderContext<HTMLDataListElement>? = null,
attrs: AttrsBuilder<HTMLDataListElement>? = null,
content: ContentBuilder<HTMLDataListElement>? = null
) {
TagElement(
@ -283,7 +288,7 @@ fun Datalist(
@Composable
fun Fieldset(
attrs: AttrBuilderContext<HTMLFieldSetElement>? = null,
attrs: AttrsBuilder<HTMLFieldSetElement>? = null,
content: ContentBuilder<HTMLFieldSetElement>? = null
) {
TagElement(
@ -295,7 +300,7 @@ fun Fieldset(
@Composable
fun Legend(
attrs: AttrBuilderContext<HTMLLegendElement>? = null,
attrs: AttrsBuilder<HTMLLegendElement>? = null,
content: ContentBuilder<HTMLLegendElement>? = null
) {
TagElement(
@ -307,7 +312,7 @@ fun Legend(
@Composable
fun Meter(
attrs: AttrBuilderContext<HTMLMeterElement>? = null,
attrs: AttrsBuilder<HTMLMeterElement>? = null,
content: ContentBuilder<HTMLMeterElement>? = null
) {
TagElement(
@ -319,7 +324,7 @@ fun Meter(
@Composable
fun Output(
attrs: AttrBuilderContext<HTMLOutputElement>? = null,
attrs: AttrsBuilder<HTMLOutputElement>? = null,
content: ContentBuilder<HTMLOutputElement>? = null
) {
TagElement(
@ -331,7 +336,7 @@ fun Output(
@Composable
fun Progress(
attrs: AttrBuilderContext<HTMLProgressElement>? = null,
attrs: AttrsBuilder<HTMLProgressElement>? = null,
content: ContentBuilder<HTMLProgressElement>? = null
) {
TagElement(
@ -343,7 +348,7 @@ fun Progress(
@Composable
fun Embed(
attrs: AttrBuilderContext<HTMLEmbedElement>? = null,
attrs: AttrsBuilder<HTMLEmbedElement>? = null,
content: ContentBuilder<HTMLEmbedElement>? = null
) {
TagElement(
@ -355,7 +360,7 @@ fun Embed(
@Composable
fun Iframe(
attrs: AttrBuilderContext<HTMLIFrameElement>? = null,
attrs: AttrsBuilder<HTMLIFrameElement>? = null,
content: ContentBuilder<HTMLIFrameElement>? = null
) {
TagElement(
@ -367,7 +372,7 @@ fun Iframe(
@Composable
fun Object(
attrs: AttrBuilderContext<HTMLObjectElement>? = null,
attrs: AttrsBuilder<HTMLObjectElement>? = null,
content: ContentBuilder<HTMLObjectElement>? = null
) {
TagElement(
@ -379,7 +384,7 @@ fun Object(
@Composable
fun Param(
attrs: AttrBuilderContext<HTMLParamElement>? = null,
attrs: AttrsBuilder<HTMLParamElement>? = null,
content: ContentBuilder<HTMLParamElement>? = null
) {
TagElement(
@ -391,7 +396,7 @@ fun Param(
@Composable
fun Picture(
attrs: AttrBuilderContext<HTMLPictureElement>? = null,
attrs: AttrsBuilder<HTMLPictureElement>? = null,
content: ContentBuilder<HTMLPictureElement>? = null
) {
TagElement(
@ -403,7 +408,7 @@ fun Picture(
@Composable
fun Source(
attrs: AttrBuilderContext<HTMLSourceElement>? = null,
attrs: AttrsBuilder<HTMLSourceElement>? = null,
content: ContentBuilder<HTMLSourceElement>? = null
) {
TagElement(
@ -426,7 +431,7 @@ fun Text(value: String) {
@Composable
fun Div(
attrs: AttrBuilderContext<HTMLDivElement>? = null,
attrs: AttrsBuilder<HTMLDivElement>? = null,
content: ContentBuilder<HTMLDivElement>? = null
) {
TagElement(
@ -439,7 +444,7 @@ fun Div(
@Composable
fun A(
href: String? = null,
attrs: AttrBuilderContext<HTMLAnchorElement>? = null,
attrs: AttrsBuilder<HTMLAnchorElement>? = null,
content: ContentBuilder<HTMLAnchorElement>? = null
) {
TagElement(
@ -458,101 +463,101 @@ fun A(
@Composable
fun Button(
attrs: AttrBuilderContext<HTMLButtonElement>? = null,
attrs: AttrsBuilder<HTMLButtonElement>? = null,
content: ContentBuilder<HTMLButtonElement>? = null
) = TagElement(elementBuilder = Button, applyAttrs = attrs, content = content)
@Composable
fun H1(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H1, applyAttrs = attrs, content = content)
@Composable
fun H2(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H2, applyAttrs = attrs, content = content)
@Composable
fun H3(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H3, applyAttrs = attrs, content = content)
@Composable
fun H4(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H4, applyAttrs = attrs, content = content)
@Composable
fun H5(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H5, applyAttrs = attrs, content = content)
@Composable
fun H6(
attrs: AttrBuilderContext<HTMLHeadingElement>? = null,
attrs: AttrsBuilder<HTMLHeadingElement>? = null,
content: ContentBuilder<HTMLHeadingElement>? = null
) = TagElement(elementBuilder = H6, applyAttrs = attrs, content = content)
@Composable
fun P(
attrs: AttrBuilderContext<HTMLParagraphElement>? = null,
attrs: AttrsBuilder<HTMLParagraphElement>? = null,
content: ContentBuilder<HTMLParagraphElement>? = null
) = TagElement(elementBuilder = P, applyAttrs = attrs, content = content)
@Composable
fun Em(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = Em, applyAttrs = attrs, content = content)
@Composable
fun I(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = I, applyAttrs = attrs, content = content)
@Composable
fun B(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = B, applyAttrs = attrs, content = content)
@Composable
fun Small(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(elementBuilder = Small, applyAttrs = attrs, content = content)
@Composable
fun Span(
attrs: AttrBuilderContext<HTMLSpanElement>? = null,
attrs: AttrsBuilder<HTMLSpanElement>? = null,
content: ContentBuilder<HTMLSpanElement>? = null
) = TagElement(elementBuilder = Span, applyAttrs = attrs, content = content)
@Composable
fun Br(attrs: AttrBuilderContext<HTMLBRElement>? = null) =
fun Br(attrs: AttrsBuilder<HTMLBRElement>? = null) =
TagElement(elementBuilder = Br, applyAttrs = attrs, content = null)
@Composable
fun Ul(
attrs: AttrBuilderContext<HTMLUListElement>? = null,
attrs: AttrsBuilder<HTMLUListElement>? = null,
content: ContentBuilder<HTMLUListElement>? = null
) = TagElement(elementBuilder = Ul, applyAttrs = attrs, content = content)
@Composable
fun Ol(
attrs: AttrBuilderContext<HTMLOListElement>? = null,
attrs: AttrsBuilder<HTMLOListElement>? = null,
content: ContentBuilder<HTMLOListElement>? = null
) = TagElement(elementBuilder = Ol, applyAttrs = attrs, content = content)
@Composable
fun Li(
attrs: AttrBuilderContext<HTMLLIElement>? = null,
attrs: AttrsBuilder<HTMLLIElement>? = null,
content: ContentBuilder<HTMLLIElement>? = null
) = TagElement(elementBuilder = Li, applyAttrs = attrs, content = content)
@ -560,7 +565,7 @@ fun Li(
fun Img(
src: String,
alt: String = "",
attrs: AttrBuilderContext<HTMLImageElement>? = null
attrs: AttrsBuilder<HTMLImageElement>? = null
) = TagElement(
elementBuilder = Img,
applyAttrs = {
@ -575,7 +580,7 @@ fun Img(
@Composable
fun Form(
action: String? = null,
attrs: AttrBuilderContext<HTMLFormElement>? = null,
attrs: AttrsBuilder<HTMLFormElement>? = null,
content: ContentBuilder<HTMLFormElement>? = null
) = TagElement(
elementBuilder = Form,
@ -590,7 +595,7 @@ fun Form(
@Composable
fun Select(
attrs: (SelectAttrsBuilder.() -> Unit)? = null,
attrs: (SelectAttrsScope.() -> Unit)? = null,
multiple: Boolean = false,
content: ContentBuilder<HTMLSelectElement>? = null
) = TagElement(
@ -598,7 +603,7 @@ fun Select(
applyAttrs = {
if (multiple) multiple()
if (attrs != null) {
val selectAttrsBuilder = with(SelectAttrsBuilder()) {
val selectAttrsBuilder = with(SelectAttrsScope()) {
attrs()
this
}
@ -611,7 +616,7 @@ fun Select(
@Composable
fun Option(
value: String,
attrs: AttrBuilderContext<HTMLOptionElement>? = null,
attrs: AttrsBuilder<HTMLOptionElement>? = null,
content: ContentBuilder<HTMLOptionElement>? = null
) = TagElement(
elementBuilder = Option,
@ -627,7 +632,7 @@ fun Option(
@Composable
fun OptGroup(
label: String,
attrs: AttrBuilderContext<HTMLOptGroupElement>? = null,
attrs: AttrsBuilder<HTMLOptGroupElement>? = null,
content: ContentBuilder<HTMLOptGroupElement>? = null
) = TagElement(
elementBuilder = OptGroup,
@ -642,7 +647,7 @@ fun OptGroup(
@Composable
fun Section(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(
elementBuilder = Section,
@ -671,7 +676,7 @@ fun Section(
@Composable
fun TextArea(
value: String? = null,
attrs: (TextAreaAttrsBuilder.() -> Unit)? = null
attrs: (TextAreaAttrsScope.() -> Unit)? = null
) {
// if firstProvidedValueWasNotNull then TextArea behaves as controlled input
val firstProvidedValueWasNotNull = remember { value != null }
@ -682,7 +687,7 @@ fun TextArea(
TagElement(
elementBuilder = TextArea,
applyAttrs = {
val textAreaAttrsBuilder = TextAreaAttrsBuilder()
val textAreaAttrsBuilder = TextAreaAttrsScope()
textAreaAttrsBuilder.onInput {
// controlled state needs to be restored after every input
keyForRestoringControlledState.value = keyForRestoringControlledState.value + 1
@ -708,7 +713,7 @@ private val textAreaRestoreControlledStateEffect: DomEffectScope.(HTMLTextAreaEl
@Composable
fun Nav(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) = TagElement(
elementBuilder = Nav,
@ -718,7 +723,7 @@ fun Nav(
@Composable
fun Pre(
attrs: AttrBuilderContext<HTMLPreElement>? = null,
attrs: AttrsBuilder<HTMLPreElement>? = null,
content: ContentBuilder<HTMLPreElement>? = null
) {
TagElement(
@ -730,7 +735,7 @@ fun Pre(
@Composable
fun Code(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
@ -742,7 +747,7 @@ fun Code(
@Composable
fun Main(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
@ -754,7 +759,7 @@ fun Main(
@Composable
fun Footer(
attrs: AttrBuilderContext<HTMLElement>? = null,
attrs: AttrsBuilder<HTMLElement>? = null,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
@ -766,7 +771,7 @@ fun Footer(
@Composable
fun Hr(
attrs: AttrBuilderContext<HTMLHRElement>? = null
attrs: AttrsBuilder<HTMLHRElement>? = null
) {
TagElement(
elementBuilder = Hr,
@ -778,7 +783,7 @@ fun Hr(
@Composable
fun Label(
forId: String? = null,
attrs: AttrBuilderContext<HTMLLabelElement>? = null,
attrs: AttrsBuilder<HTMLLabelElement>? = null,
content: ContentBuilder<HTMLLabelElement>? = null
) {
TagElement(
@ -797,7 +802,7 @@ fun Label(
@Composable
fun Table(
attrs: AttrBuilderContext<HTMLTableElement>? = null,
attrs: AttrsBuilder<HTMLTableElement>? = null,
content: ContentBuilder<HTMLTableElement>? = null
) {
TagElement(
@ -809,7 +814,7 @@ fun Table(
@Composable
fun Caption(
attrs: AttrBuilderContext<HTMLTableCaptionElement>? = null,
attrs: AttrsBuilder<HTMLTableCaptionElement>? = null,
content: ContentBuilder<HTMLTableCaptionElement>? = null
) {
TagElement(
@ -821,7 +826,7 @@ fun Caption(
@Composable
fun Col(
attrs: AttrBuilderContext<HTMLTableColElement>? = null
attrs: AttrsBuilder<HTMLTableColElement>? = null
) {
TagElement(
elementBuilder = Col,
@ -832,7 +837,7 @@ fun Col(
@Composable
fun Colgroup(
attrs: AttrBuilderContext<HTMLTableColElement>? = null,
attrs: AttrsBuilder<HTMLTableColElement>? = null,
content: ContentBuilder<HTMLTableColElement>? = null
) {
TagElement(
@ -844,7 +849,7 @@ fun Colgroup(
@Composable
fun Tr(
attrs: AttrBuilderContext<HTMLTableRowElement>? = null,
attrs: AttrsBuilder<HTMLTableRowElement>? = null,
content: ContentBuilder<HTMLTableRowElement>? = null
) {
TagElement(
@ -856,7 +861,7 @@ fun Tr(
@Composable
fun Thead(
attrs: AttrBuilderContext<HTMLTableSectionElement>? = null,
attrs: AttrsBuilder<HTMLTableSectionElement>? = null,
content: ContentBuilder<HTMLTableSectionElement>? = null
) {
TagElement(
@ -868,7 +873,7 @@ fun Thead(
@Composable
fun Th(
attrs: AttrBuilderContext<HTMLTableCellElement>? = null,
attrs: AttrsBuilder<HTMLTableCellElement>? = null,
content: ContentBuilder<HTMLTableCellElement>? = null
) {
TagElement(
@ -880,7 +885,7 @@ fun Th(
@Composable
fun Td(
attrs: AttrBuilderContext<HTMLTableCellElement>? = null,
attrs: AttrsBuilder<HTMLTableCellElement>? = null,
content: ContentBuilder<HTMLTableCellElement>? = null
) {
TagElement(
@ -892,7 +897,7 @@ fun Td(
@Composable
fun Tbody(
attrs: AttrBuilderContext<HTMLTableSectionElement>? = null,
attrs: AttrsBuilder<HTMLTableSectionElement>? = null,
content: ContentBuilder<HTMLTableSectionElement>? = null
) {
TagElement(
@ -904,7 +909,7 @@ fun Tbody(
@Composable
fun Tfoot(
attrs: AttrBuilderContext<HTMLTableSectionElement>? = null,
attrs: AttrsBuilder<HTMLTableSectionElement>? = null,
content: ContentBuilder<HTMLTableSectionElement>? = null
) {
TagElement(
@ -922,7 +927,7 @@ fun Tfoot(
*/
@Composable
fun Style(
applyAttrs: (AttrsBuilder<HTMLStyleElement>.() -> Unit)? = null,
applyAttrs: (AttrsScope<HTMLStyleElement>.() -> Unit)? = null,
cssRules: CSSRuleDeclarationList
) {
TagElement(
@ -951,7 +956,7 @@ fun Style(
*/
@Composable
inline fun Style(
noinline applyAttrs: (AttrsBuilder<HTMLStyleElement>.() -> Unit)? = null,
noinline applyAttrs: (AttrsScope<HTMLStyleElement>.() -> Unit)? = null,
rulesBuild: StyleSheetBuilder.() -> Unit
) {
val builder = StyleSheetBuilderImpl()
@ -994,7 +999,7 @@ inline fun Style(
@Composable
fun <K> Input(
type: InputType<K>,
attrs: InputAttrsBuilder<K>.() -> Unit
attrs: InputAttrsScope<K>.() -> Unit
) {
// changes to this key trigger [inputRestoreControlledStateEffect]
val keyForRestoringControlledState: MutableState<Int> = remember { mutableStateOf(0) }
@ -1002,7 +1007,7 @@ fun <K> Input(
TagElement(
elementBuilder = Input,
applyAttrs = {
val inputAttrsBuilder = InputAttrsBuilder(type)
val inputAttrsBuilder = InputAttrsScope(type)
inputAttrsBuilder.type(type)
inputAttrsBuilder.onInput {
// 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.NonRestartableComposable
import org.jetbrains.compose.web.attributes.builders.InputAttrsBuilder
import org.jetbrains.compose.web.attributes.builders.InputAttrsScope
import org.jetbrains.compose.web.attributes.*
private fun InputAttrsBuilder<String>.applyAttrsWithStringValue(
private fun InputAttrsScope<String>.applyAttrsWithStringValue(
value: String,
attrs: InputAttrsBuilder<String>.() -> Unit
attrs: InputAttrsScope<String>.() -> Unit
) {
value(value)
attrs()
@ -22,7 +22,7 @@ private fun InputAttrsBuilder<String>.applyAttrsWithStringValue(
*/
@Composable
@NonRestartableComposable
fun CheckboxInput(checked: Boolean = false, attrs: InputAttrsBuilder<Boolean>.() -> Unit = {}) {
fun CheckboxInput(checked: Boolean = false, attrs: InputAttrsScope<Boolean>.() -> Unit = {}) {
Input(
type = InputType.Checkbox,
attrs = {
@ -41,7 +41,7 @@ fun CheckboxInput(checked: Boolean = false, attrs: InputAttrsBuilder<Boolean>.()
*/
@Composable
@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) })
}
@ -54,7 +54,7 @@ fun DateInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/
@Composable
@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) })
}
@ -67,7 +67,7 @@ fun DateTimeLocalInput(value: String = "", attrs: InputAttrsBuilder<String>.() -
*/
@Composable
@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) })
}
@ -80,7 +80,7 @@ fun EmailInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/
@Composable
@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) })
}
@ -93,7 +93,7 @@ fun FileInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/
@Composable
@NonRestartableComposable
fun HiddenInput(attrs: InputAttrsBuilder<String>.() -> Unit = {}) {
fun HiddenInput(attrs: InputAttrsScope<String>.() -> Unit = {}) {
Input(type = InputType.Hidden, attrs = attrs)
}
@ -106,7 +106,7 @@ fun HiddenInput(attrs: InputAttrsBuilder<String>.() -> Unit = {}) {
*/
@Composable
@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) })
}
@ -123,7 +123,7 @@ fun NumberInput(
value: Number? = null,
min: Number? = null,
max: Number? = null,
attrs: InputAttrsBuilder<Number?>.() -> Unit = {}
attrs: InputAttrsScope<Number?>.() -> Unit = {}
) {
Input(
type = InputType.Number,
@ -145,7 +145,7 @@ fun NumberInput(
*/
@Composable
@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) })
}
@ -158,7 +158,7 @@ fun PasswordInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Uni
*/
@Composable
@NonRestartableComposable
fun RadioInput(checked: Boolean = false, attrs: InputAttrsBuilder<Boolean>.() -> Unit = {}) {
fun RadioInput(checked: Boolean = false, attrs: InputAttrsScope<Boolean>.() -> Unit = {}) {
Input(
type = InputType.Radio,
attrs = {
@ -182,7 +182,7 @@ fun RangeInput(
min: Number? = null,
max: Number? = null,
step: Number = 1,
attrs: InputAttrsBuilder<Number?>.() -> Unit = {}
attrs: InputAttrsScope<Number?>.() -> Unit = {}
) {
Input(
type = InputType.Range,
@ -205,7 +205,7 @@ fun RangeInput(
*/
@Composable
@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) })
}
@ -215,7 +215,7 @@ fun SearchInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit
*/
@Composable
@NonRestartableComposable
fun SubmitInput(attrs: InputAttrsBuilder<Unit>.() -> Unit = {}) {
fun SubmitInput(attrs: InputAttrsScope<Unit>.() -> Unit = {}) {
Input(type = InputType.Submit, attrs = attrs)
}
@ -228,7 +228,7 @@ fun SubmitInput(attrs: InputAttrsBuilder<Unit>.() -> Unit = {}) {
*/
@Composable
@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) })
}
@ -241,7 +241,7 @@ fun TelInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {
*/
@Composable
@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) })
}
@ -254,7 +254,7 @@ fun TextInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/
@Composable
@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) })
}
@ -267,7 +267,7 @@ fun TimeInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit =
*/
@Composable
@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) })
}
@ -280,6 +280,6 @@ fun UrlInput(value: String = "", attrs: InputAttrsBuilder<String>.() -> Unit = {
*/
@Composable
@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) })
}

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 org.jetbrains.compose.web.ExperimentalComposeWebApi
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
typealias RadioInputAttrsBuilder = (InputAttrsBuilder<Boolean>.() -> Unit)
typealias RadioInputAttrsBuilder = (InputAttrsScope<Boolean>.() -> Unit)
/**
* @param value - sets `value` attribute

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

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

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

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

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

@ -343,7 +343,7 @@ class InputsTests {
})
Div(attrs = {
addEventListener(EventsListenerBuilder.INPUT) {
addEventListener(EventsListenerScope.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
@ExperimentalComposeWebSvgApi
fun Svg(
viewBox: String? = null,
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
viewBox: String? = null,
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = Svg,
@ -78,9 +78,9 @@ fun Svg(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.SvgA(
href: String,
attrs: AttrBuilderContext<SVGAElement>? = null,
content: ContentBuilder<SVGAElement>? = null
href: String,
attrs: AttrsBuilder<SVGAElement>? = null,
content: ContentBuilder<SVGAElement>? = null
) {
TagElement(
elementBuilder = A,
@ -95,11 +95,11 @@ fun ElementScope<SVGElement>.SvgA(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Circle(
cx: CSSLengthOrPercentageValue,
cy: CSSLengthOrPercentageValue,
r: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGCircleElement>? = null,
content: ContentBuilder<SVGCircleElement>? = null
cx: CSSLengthOrPercentageValue,
cy: CSSLengthOrPercentageValue,
r: CSSLengthOrPercentageValue,
attrs: AttrsBuilder<SVGCircleElement>? = null,
content: ContentBuilder<SVGCircleElement>? = null
) {
TagElement(
elementBuilder = Circle,
@ -117,11 +117,11 @@ fun ElementScope<SVGElement>.Circle(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Circle(
cx: Number,
cy: Number,
r: Number,
attrs: AttrBuilderContext<SVGCircleElement>? = null,
content: ContentBuilder<SVGCircleElement>? = null
cx: Number,
cy: Number,
r: Number,
attrs: AttrsBuilder<SVGCircleElement>? = null,
content: ContentBuilder<SVGCircleElement>? = null
) {
TagElement(
elementBuilder = Circle,
@ -139,10 +139,10 @@ fun ElementScope<SVGElement>.Circle(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.SvgText(
text: String,
x: Number = 0,
y: Number = 0,
attrs: AttrBuilderContext<SVGTextElement>? = null,
text: String,
x: Number = 0,
y: Number = 0,
attrs: AttrsBuilder<SVGTextElement>? = null,
) {
TagElement(
elementBuilder = Text,
@ -160,9 +160,9 @@ fun ElementScope<SVGElement>.SvgText(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.View(
id: String,
viewBox: String,
attrs: AttrBuilderContext<SVGViewElement>? = null,
id: String,
viewBox: String,
attrs: AttrsBuilder<SVGViewElement>? = null,
) {
TagElement(
elementBuilder = View,
@ -178,12 +178,12 @@ fun ElementScope<SVGElement>.View(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Rect(
x: Number,
y: Number,
width: Number,
height: Number,
attrs: AttrBuilderContext<SVGRectElement>? = null,
content: ContentBuilder<SVGRectElement>? = null
x: Number,
y: Number,
width: Number,
height: Number,
attrs: AttrsBuilder<SVGRectElement>? = null,
content: ContentBuilder<SVGRectElement>? = null
) {
TagElement(
elementBuilder = Rect,
@ -201,12 +201,12 @@ fun ElementScope<SVGElement>.Rect(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Rect(
x: CSSLengthOrPercentageValue,
y: CSSLengthOrPercentageValue,
width: CSSLengthOrPercentageValue,
height: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGRectElement>? = null,
content: ContentBuilder<SVGRectElement>? = null
x: CSSLengthOrPercentageValue,
y: CSSLengthOrPercentageValue,
width: CSSLengthOrPercentageValue,
height: CSSLengthOrPercentageValue,
attrs: AttrsBuilder<SVGRectElement>? = null,
content: ContentBuilder<SVGRectElement>? = null
) {
TagElement(
elementBuilder = Rect,
@ -224,12 +224,12 @@ fun ElementScope<SVGElement>.Rect(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Ellipse(
cx: CSSLengthOrPercentageValue,
cy: CSSLengthOrPercentageValue,
rx: CSSLengthOrPercentageValue,
ry: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGEllipseElement>? = null,
content: ContentBuilder<SVGEllipseElement>? = null
cx: CSSLengthOrPercentageValue,
cy: CSSLengthOrPercentageValue,
rx: CSSLengthOrPercentageValue,
ry: CSSLengthOrPercentageValue,
attrs: AttrsBuilder<SVGEllipseElement>? = null,
content: ContentBuilder<SVGEllipseElement>? = null
) {
TagElement(
elementBuilder = Ellipse,
@ -247,12 +247,12 @@ fun ElementScope<SVGElement>.Ellipse(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Ellipse(
cx: Number,
cy: Number,
rx: Number,
ry: Number,
attrs: AttrBuilderContext<SVGEllipseElement>? = null,
content: ContentBuilder<SVGEllipseElement>? = null
cx: Number,
cy: Number,
rx: Number,
ry: Number,
attrs: AttrsBuilder<SVGEllipseElement>? = null,
content: ContentBuilder<SVGEllipseElement>? = null
) {
TagElement(
elementBuilder = Ellipse,
@ -271,9 +271,9 @@ fun ElementScope<SVGElement>.Ellipse(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Symbol(
id: String? = null,
attrs: AttrBuilderContext<SVGSymbolElement>? = null,
content: ContentBuilder<SVGSymbolElement>? = null
id: String? = null,
attrs: AttrsBuilder<SVGSymbolElement>? = null,
content: ContentBuilder<SVGSymbolElement>? = null
) {
TagElement(
elementBuilder = Symbol,
@ -288,9 +288,9 @@ fun ElementScope<SVGElement>.Symbol(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Use(
href: String,
attrs: AttrBuilderContext<SVGUseElement>? = null,
content: ContentBuilder<SVGUseElement>? = null
href: String,
attrs: AttrsBuilder<SVGUseElement>? = null,
content: ContentBuilder<SVGUseElement>? = null
) {
TagElement(
elementBuilder = Use,
@ -305,12 +305,12 @@ fun ElementScope<SVGElement>.Use(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Line(
x1: CSSLengthOrPercentageValue,
y1: CSSLengthOrPercentageValue,
x2: CSSLengthOrPercentageValue,
y2: CSSLengthOrPercentageValue,
attrs: AttrBuilderContext<SVGLineElement>? = null,
content: ContentBuilder<SVGLineElement>? = null
x1: CSSLengthOrPercentageValue,
y1: CSSLengthOrPercentageValue,
x2: CSSLengthOrPercentageValue,
y2: CSSLengthOrPercentageValue,
attrs: AttrsBuilder<SVGLineElement>? = null,
content: ContentBuilder<SVGLineElement>? = null
) {
TagElement(
elementBuilder = Line,
@ -328,12 +328,12 @@ fun ElementScope<SVGElement>.Line(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Line(
x1: Number,
y1: Number,
x2: Number,
y2: Number,
attrs: AttrBuilderContext<SVGLineElement>? = null,
content: ContentBuilder<SVGLineElement>? = null
x1: Number,
y1: Number,
x2: Number,
y2: Number,
attrs: AttrsBuilder<SVGLineElement>? = null,
content: ContentBuilder<SVGLineElement>? = null
) {
TagElement(
elementBuilder = Line,
@ -352,9 +352,9 @@ fun ElementScope<SVGElement>.Line(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.ClipPath(
id: String,
attrs: AttrBuilderContext<SVGClipPathElement>? = null,
content: ContentBuilder<SVGClipPathElement>? = null
id: String,
attrs: AttrsBuilder<SVGClipPathElement>? = null,
content: ContentBuilder<SVGClipPathElement>? = null
) {
TagElement(
elementBuilder = ClipPath,
@ -369,9 +369,9 @@ fun ElementScope<SVGElement>.ClipPath(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Path(
d: String,
attrs: AttrBuilderContext<SVGPathElement>? = null,
content: ContentBuilder<SVGPathElement>? = null
d: String,
attrs: AttrsBuilder<SVGPathElement>? = null,
content: ContentBuilder<SVGPathElement>? = null
) {
TagElement(
elementBuilder = Path,
@ -386,8 +386,8 @@ fun ElementScope<SVGElement>.Path(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.G(
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = G,
@ -399,9 +399,9 @@ fun ElementScope<SVGElement>.G(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Image(
href: String,
attrs: AttrBuilderContext<SVGImageElement>? = null,
content: ContentBuilder<SVGImageElement>? = null
href: String,
attrs: AttrsBuilder<SVGImageElement>? = null,
content: ContentBuilder<SVGImageElement>? = null
) {
TagElement(
elementBuilder = Image,
@ -416,9 +416,9 @@ fun ElementScope<SVGElement>.Image(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Mask(
id: String? = null,
attrs: AttrBuilderContext<SVGMaskElement>? = null,
content: ContentBuilder<SVGMaskElement>? = null
id: String? = null,
attrs: AttrsBuilder<SVGMaskElement>? = null,
content: ContentBuilder<SVGMaskElement>? = null
) {
TagElement(
elementBuilder = Mask,
@ -433,8 +433,8 @@ fun ElementScope<SVGElement>.Mask(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Defs(
attrs: AttrBuilderContext<SVGDefsElement>? = null,
content: ContentBuilder<SVGDefsElement>? = null
attrs: AttrsBuilder<SVGDefsElement>? = null,
content: ContentBuilder<SVGDefsElement>? = null
) {
TagElement(
elementBuilder = Defs,
@ -446,9 +446,9 @@ fun ElementScope<SVGElement>.Defs(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Pattern(
id: String,
attrs: AttrBuilderContext<SVGPatternElement>? = null,
content: ContentBuilder<SVGPatternElement>? = null
id: String,
attrs: AttrsBuilder<SVGPatternElement>? = null,
content: ContentBuilder<SVGPatternElement>? = null
) {
TagElement(
elementBuilder = Pattern,
@ -463,9 +463,9 @@ fun ElementScope<SVGElement>.Pattern(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Polygon(
vararg points: Number,
attrs: AttrBuilderContext<SVGPolygonElement>? = null,
content: ContentBuilder<SVGPolygonElement>? = null
vararg points: Number,
attrs: AttrsBuilder<SVGPolygonElement>? = null,
content: ContentBuilder<SVGPolygonElement>? = null
) {
TagElement(
elementBuilder = Polygon,
@ -480,9 +480,9 @@ fun ElementScope<SVGElement>.Polygon(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Polyline(
vararg points: Number,
attrs: AttrBuilderContext<SVGPolylineElement>? = null,
content: ContentBuilder<SVGPolylineElement>? = null
vararg points: Number,
attrs: AttrsBuilder<SVGPolylineElement>? = null,
content: ContentBuilder<SVGPolylineElement>? = null
) {
TagElement(
elementBuilder = Polyline,
@ -497,9 +497,9 @@ fun ElementScope<SVGElement>.Polyline(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.TextPath(
href: String,
text: String,
attrs: AttrBuilderContext<SVGTextPathElement>? = null,
href: String,
text: String,
attrs: AttrsBuilder<SVGTextPathElement>? = null,
) {
TagElement(
elementBuilder = TextPath,
@ -516,8 +516,8 @@ fun ElementScope<SVGElement>.TextPath(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Animate(
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = Animate,
@ -529,8 +529,8 @@ fun ElementScope<SVGElement>.Animate(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.AnimateMotion(
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = AnimateMotion,
@ -542,8 +542,8 @@ fun ElementScope<SVGElement>.AnimateMotion(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.AnimateTransform(
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = AnimateTransform,
@ -555,9 +555,9 @@ fun ElementScope<SVGElement>.AnimateTransform(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.LinearGradient(
id: String? = null,
attrs: AttrBuilderContext<SVGLinearGradientElement>? = null,
content: ContentBuilder<SVGLinearGradientElement>? = null
id: String? = null,
attrs: AttrsBuilder<SVGLinearGradientElement>? = null,
content: ContentBuilder<SVGLinearGradientElement>? = null
) {
TagElement(
elementBuilder = LinearGradient,
@ -573,9 +573,9 @@ fun ElementScope<SVGElement>.LinearGradient(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.RadialGradient(
id: String? = null,
attrs: AttrBuilderContext<SVGRadialGradientElement>? = null,
content: ContentBuilder<SVGRadialGradientElement>? = null
id: String? = null,
attrs: AttrsBuilder<SVGRadialGradientElement>? = null,
content: ContentBuilder<SVGRadialGradientElement>? = null
) {
TagElement(
elementBuilder = RadialGradient,
@ -590,8 +590,8 @@ fun ElementScope<SVGElement>.RadialGradient(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Stop(
attrs: AttrBuilderContext<SVGStopElement>? = null,
content: ContentBuilder<SVGStopElement>? = null
attrs: AttrsBuilder<SVGStopElement>? = null,
content: ContentBuilder<SVGStopElement>? = null
) {
TagElement(
elementBuilder = Stop,
@ -603,8 +603,8 @@ fun ElementScope<SVGElement>.Stop(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Switch(
attrs: AttrBuilderContext<SVGSwitchElement>? = null,
content: ContentBuilder<SVGSwitchElement>? = null
attrs: AttrsBuilder<SVGSwitchElement>? = null,
content: ContentBuilder<SVGSwitchElement>? = null
) {
TagElement(
elementBuilder = Switch,
@ -616,8 +616,8 @@ fun ElementScope<SVGElement>.Switch(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Title(
text: String,
attrs: AttrBuilderContext<SVGTitleElement>? = null,
text: String,
attrs: AttrsBuilder<SVGTitleElement>? = null,
) {
TagElement(
elementBuilder = Title,
@ -631,8 +631,8 @@ fun ElementScope<SVGElement>.Title(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Tspan(
attrs: AttrBuilderContext<SVGTSpanElement>? = null,
content: ContentBuilder<SVGTSpanElement>? = null
attrs: AttrsBuilder<SVGTSpanElement>? = null,
content: ContentBuilder<SVGTSpanElement>? = null
) {
TagElement(
elementBuilder = Tspan,
@ -644,8 +644,8 @@ fun ElementScope<SVGElement>.Tspan(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Desc(
content: String,
attrs: AttrBuilderContext<SVGDescElement>? = null,
content: String,
attrs: AttrsBuilder<SVGDescElement>? = null,
) {
TagElement(
elementBuilder = Desc,
@ -659,8 +659,8 @@ fun ElementScope<SVGElement>.Desc(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Marker(
attrs: AttrBuilderContext<SVGMarkerElement>? = null,
content: ContentBuilder<SVGMarkerElement>? = null
attrs: AttrsBuilder<SVGMarkerElement>? = null,
content: ContentBuilder<SVGMarkerElement>? = null
) {
TagElement(
elementBuilder = Marker,
@ -672,8 +672,8 @@ fun ElementScope<SVGElement>.Marker(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Mpath(
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = Mpath,
@ -685,8 +685,8 @@ fun ElementScope<SVGElement>.Mpath(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Filter(
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = Filter,
@ -698,10 +698,10 @@ fun ElementScope<SVGElement>.Filter(
@Composable
@ExperimentalComposeWebSvgApi
fun ElementScope<SVGElement>.Set(
attributeName: String,
to: String,
attrs: AttrBuilderContext<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
attributeName: String,
to: String,
attrs: AttrsBuilder<SVGElement>? = null,
content: ContentBuilder<SVGElement>? = null
) {
TagElement(
elementBuilder = Set,
@ -717,13 +717,13 @@ fun ElementScope<SVGElement>.Set(
@Composable
@ExperimentalComposeWebSvgApi
fun <T : SVGElement> SvgElement(
name: String,
attrs: AttrBuilderContext<T>? = null,
content: ContentBuilder<T>? = null
name: String,
attrs: AttrsBuilder<T>? = null,
content: ContentBuilder<T>? = null
) {
TagElement(
elementBuilder = ElementBuilderNS(name, SVG_NS),
applyAttrs = attrs,
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.Color.RGB
import org.jetbrains.compose.common.internal.castOrCreate
import org.jetbrains.compose.web.attributes.AttrsBuilder
import org.jetbrains.compose.web.attributes.AttrsScope
@ExperimentalComposeWebWidgetsApi
@Deprecated(message = webWidgetsDeprecationMessage)
@ -21,10 +21,10 @@ actual fun Modifier.background(color: Color): Modifier = castOrCreate().apply {
@ExperimentalComposeWebWidgetsApi
@Deprecated(message = webWidgetsDeprecationMessage)
fun Modifier.asAttributeBuilderApplier(
passThroughHandler: (AttrsBuilder<*>.() -> Unit)? = null
): AttrsBuilder<*>.() -> Unit =
passThroughHandler: (AttrsScope<*>.() -> Unit)? = null
): AttrsScope<*>.() -> Unit =
castOrCreate().let { modifier ->
val st: AttrsBuilder<*>.() -> Unit = {
val st: AttrsScope<*>.() -> Unit = {
modifier.attrHandlers.forEach { it.invoke(this) }
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.Modifier
import org.jetbrains.compose.web.css.StyleBuilder
import org.jetbrains.compose.web.attributes.AttrsBuilder
import org.jetbrains.compose.web.attributes.AttrsScope
@ExperimentalComposeWebWidgetsApi
@Deprecated(message = webWidgetsDeprecationMessage)
class ActualModifier : Modifier {
val styleHandlers = mutableListOf<StyleBuilder.() -> Unit>()
val attrHandlers = mutableListOf<AttrsBuilder<*>.() -> Unit>()
val attrHandlers = mutableListOf<AttrsScope<*>.() -> Unit>()
fun add(builder: StyleBuilder.() -> Unit) {
styleHandlers.add(builder)
}
fun addAttributeBuilder(builder: AttrsBuilder<*>.() -> Unit) {
fun addAttributeBuilder(builder: AttrsScope<*>.() -> Unit) {
attrHandlers.add(builder)
}
}

Loading…
Cancel
Save