Browse Source

web: hide internal properties from AttrsScope, EventsListenerScope, StyleScope (#1802)

Co-authored-by: Oleksandr Karpovich <oleksandr.karpovich@jetbrains.com>
web_reuse_attrsScope_instance
Oleksandr Karpovich 3 years ago committed by GitHub
parent
commit
7ea30be924
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 53
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/AttrsScope.kt
  2. 14
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/EventsListenerScope.kt
  3. 5
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/InputAttrsScope.kt
  4. 4
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/SelectAttrsScope.kt
  5. 2
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/TextAreaAttrsScope.kt
  6. 21
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleScope.kt
  7. 5
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Base.kt
  8. 14
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/Elements.kt
  9. 7
      web/core/src/jsTest/kotlin/elements/AttributesTests.kt
  10. 1
      web/settings.gradle.kts

53
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/AttrsScope.kt

@ -28,16 +28,6 @@ typealias AttrsBuilder<T> = AttrsScopeBuilder<T>
* *
*/ */
interface AttrsScope<TElement : Element>: EventsListenerScope { 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 [StyleScope] context * [style] add inline CSS-style properties to the element via [StyleScope] context
* *
@ -50,9 +40,7 @@ interface AttrsScope<TElement : Element>: EventsListenerScope {
* *
* `attr("style", ...)` overrides everything added in `style { }` blocks * `attr("style", ...)` overrides everything added in `style { }` blocks
*/ */
fun style(builder: StyleScope.() -> Unit) { fun style(builder: StyleScope.() -> Unit)
styleScope.apply(builder)
}
/** /**
* [classes] adds all values passed as params to the element's classList. * [classes] adds all values passed as params to the element's classList.
@ -105,7 +93,7 @@ interface AttrsScope<TElement : Element>: EventsListenerScope {
fun attr(attr: String, value: String): AttrsScope<TElement> fun attr(attr: String, value: String): AttrsScope<TElement>
/** /**
* [prop] allows setting values of element's properties which can't be set by ussing [attr]. * [prop] allows setting values of element's properties which can't be set using [attr].
* [update] is a lambda with two parameters: `element` and `value`. `element` is a reference to a native element. * [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`. * Some examples of properties that can set using [prop]: `value`, `checked`, `innerText`.
* *
@ -124,9 +112,6 @@ interface AttrsScope<TElement : Element>: EventsListenerScope {
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <E : HTMLElement, V> prop(update: (E, V) -> Unit, value: V) fun <E : HTMLElement, V> prop(update: (E, V) -> Unit, value: V)
@ComposeWebInternalApi
fun copyFrom(attrsScope: AttrsScope<TElement>)
companion object { companion object {
const val CLASS = "class" const val CLASS = "class"
const val ID = "id" const val ID = "id"
@ -141,13 +126,13 @@ interface AttrsScope<TElement : Element>: EventsListenerScope {
} }
} }
open class AttrsScopeBuilder<TElement : Element> : AttrsScope<TElement>, EventsListenerScope by EventsListenerScopeBuilder() { open class AttrsScopeBuilder<TElement : Element>(
override val attributesMap = mutableMapOf<String, String>() internal val eventsListenerScopeBuilder: EventsListenerScopeBuilder = EventsListenerScopeBuilder()
override val styleScope: StyleScope = StyleScopeBuilder() ) : AttrsScope<TElement>, EventsListenerScope by eventsListenerScopeBuilder {
internal val attributesMap = mutableMapOf<String, String>()
override val propertyUpdates = mutableListOf<Pair<(Element, Any) -> Unit, Any>>() internal val styleScope: StyleScopeBuilder = StyleScopeBuilder()
override var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)? = null internal val propertyUpdates = mutableListOf<Pair<(Element, Any) -> Unit, Any>>()
internal var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)? = null
internal val classes: MutableList<String> = mutableListOf() internal val classes: MutableList<String> = mutableListOf()
/** /**
@ -160,6 +145,22 @@ open class AttrsScopeBuilder<TElement : Element> : AttrsScope<TElement>, EventsL
this.classes.addAll(classes) this.classes.addAll(classes)
} }
/**
* [style] add inline CSS-style properties to the element via [StyleScope] context
*
* Example:
* ```
* Div({
* style { maxWidth(5.px) }
* })
* ```
*
* `attr("style", ...)` overrides everything added in `style { }` blocks
*/
override fun style(builder: StyleScope.() -> Unit) {
styleScope.apply(builder)
}
/** /**
* [ref] can be used to retrieve a reference to a html element. * [ref] can be used to retrieve a reference to a html element.
* The lambda that `ref` takes in is not Composable. It will be called only once when an element added into a composition. * The lambda that `ref` takes in is not Composable. It will be called only once when an element added into a composition.
@ -212,14 +213,14 @@ open class AttrsScopeBuilder<TElement : Element> : AttrsScope<TElement>, EventsL
} }
@ComposeWebInternalApi @ComposeWebInternalApi
override fun copyFrom(attrsScope: AttrsScope<TElement>) { internal fun copyFrom(attrsScope: AttrsScopeBuilder<TElement>) {
refEffect = attrsScope.refEffect refEffect = attrsScope.refEffect
styleScope.copyFrom(attrsScope.styleScope) styleScope.copyFrom(attrsScope.styleScope)
attributesMap.putAll(attrsScope.attributesMap) attributesMap.putAll(attrsScope.attributesMap)
propertyUpdates.addAll(attrsScope.propertyUpdates) propertyUpdates.addAll(attrsScope.propertyUpdates)
copyListenersFrom(attrsScope) eventsListenerScopeBuilder.copyListenersFrom(attrsScope.eventsListenerScopeBuilder)
} }
} }

14
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/EventsListenerScope.kt

@ -25,9 +25,6 @@ typealias EventsListenerBuilder = EventsListenerScopeBuilder
* use [addEventListener] * use [addEventListener]
*/ */
interface EventsListenerScope { interface EventsListenerScope {
@ComposeWebInternalApi
val listeners: List<SyntheticEventListener<*>>
@ComposeWebInternalApi @ComposeWebInternalApi
fun registerEventListener(listener: SyntheticEventListener<*>) fun registerEventListener(listener: SyntheticEventListener<*>)
@ -197,8 +194,6 @@ interface EventsListenerScope {
registerEventListener(SyntheticEventListener(SCROLL, listener)) registerEventListener(SyntheticEventListener(SCROLL, listener))
} }
fun collectListeners(): List<SyntheticEventListener<*>> = listeners
/** /**
* [addEventListener] used for adding arbitrary events to the element. It resembles the standard DOM addEventListener method * [addEventListener] used for adding arbitrary events to the element. It resembles the standard DOM addEventListener method
* @param eventName - the name of the event * @param eventName - the name of the event
@ -219,9 +214,6 @@ interface EventsListenerScope {
registerEventListener(SyntheticEventListener(eventName, listener)) registerEventListener(SyntheticEventListener(eventName, listener))
} }
@ComposeWebInternalApi
fun copyListenersFrom(from: EventsListenerScope)
companion object { companion object {
const val COPY = "copy" const val COPY = "copy"
const val CUT = "cut" const val CUT = "cut"
@ -277,13 +269,15 @@ interface EventsListenerScope {
} }
open class EventsListenerScopeBuilder : EventsListenerScope { open class EventsListenerScopeBuilder : EventsListenerScope {
override val listeners: MutableList<SyntheticEventListener<*>> = mutableListOf() private val listeners: MutableList<SyntheticEventListener<*>> = mutableListOf()
override fun registerEventListener(listener: SyntheticEventListener<*>) { override fun registerEventListener(listener: SyntheticEventListener<*>) {
listeners.add(listener) listeners.add(listener)
} }
override fun copyListenersFrom(from: EventsListenerScope) { internal fun copyListenersFrom(from: EventsListenerScopeBuilder) {
listeners.addAll(from.listeners) listeners.addAll(from.listeners)
} }
internal fun collectListeners(): List<SyntheticEventListener<*>> = listeners
} }

5
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/InputAttrsScope.kt

@ -38,8 +38,9 @@ typealias InputAttrsBuilder<T> = InputAttrsScope<T>
* [onSelect] - add `select` event listener * [onSelect] - add `select` event listener
*/ */
class InputAttrsScope<ValueType>( class InputAttrsScope<ValueType>(
val inputType: InputType<ValueType> val inputType: InputType<ValueType>,
) : AttrsScopeBuilder<HTMLInputElement>() { attrsScope: AttrsScope<HTMLInputElement>
) : AttrsScope<HTMLInputElement> by attrsScope {
fun value(value: String): InputAttrsScope<ValueType> { fun value(value: String): InputAttrsScope<ValueType> {
when (inputType) { when (inputType) {

4
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/builders/SelectAttrsScope.kt

@ -5,7 +5,7 @@
package androidx.compose.web.attributes package androidx.compose.web.attributes
import org.jetbrains.compose.web.attributes.AttrsScopeBuilder import org.jetbrains.compose.web.attributes.AttrsScope
import org.jetbrains.compose.web.attributes.EventsListenerScope.Companion.CHANGE 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.INPUT
import org.jetbrains.compose.web.attributes.SyntheticEventListener import org.jetbrains.compose.web.attributes.SyntheticEventListener
@ -20,7 +20,7 @@ import org.w3c.dom.events.Event
) )
typealias SelectAttrsBuilder = SelectAttrsScope typealias SelectAttrsBuilder = SelectAttrsScope
class SelectAttrsScope : AttrsScopeBuilder<HTMLSelectElement>() { class SelectAttrsScope(attrsScope: AttrsScope<HTMLSelectElement>) : AttrsScope<HTMLSelectElement> by attrsScope {
fun onInput( fun onInput(
listener: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit listener: (SyntheticInputEvent<String?, HTMLSelectElement>) -> Unit

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

@ -26,7 +26,7 @@ import org.w3c.dom.HTMLTextAreaElement
) )
typealias TextAreaAttrsBuilder = TextAreaAttrsScope typealias TextAreaAttrsBuilder = TextAreaAttrsScope
class TextAreaAttrsScope : AttrsScopeBuilder<HTMLTextAreaElement>() { class TextAreaAttrsScope(attrsScope: AttrsScope<HTMLTextAreaElement>) : AttrsScope<HTMLTextAreaElement> by attrsScope {
fun value(value: String): AttrsScope<HTMLTextAreaElement> { fun value(value: String): AttrsScope<HTMLTextAreaElement> {
prop(setInputValue, value) prop(setInputValue, value)

21
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleScope.kt

@ -10,13 +10,19 @@ package org.jetbrains.compose.web.css
import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi import org.jetbrains.compose.web.internal.runtime.ComposeWebInternalApi
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
@Deprecated(
message = "Renamed to StyleScope",
replaceWith = ReplaceWith("StyleScope", "org.jetbrains.compose.web.css.StyleScope")
)
typealias StyleBuilder = StyleScope
/** /**
* StyleBuilder serves for two main purposes. Passed as a builder context (in [AttrsScope]), it * StyleScope serves for two main purposes. Passed as a builder context (in [AttrsScope]), it
* makes it possible to: * makes it possible to:
* 1. Add inlined css properties to the element (@see [property]) * 1. Add inlined css properties to the element (@see [property])
* 2. Set values to CSS variables (@see [variable]) * 2. Set values to CSS variables (@see [variable])
*/ */
interface StyleBuilder { interface StyleScope {
/** /**
* Adds arbitrary CSS property to the inline style of the element * Adds arbitrary CSS property to the inline style of the element
* @param propertyName - the name of css property as [per spec](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference) * @param propertyName - the name of css property as [per spec](https://developer.mozilla.org/en-US/docs/Web/CSS/Reference)
@ -131,20 +137,14 @@ interface StyleHolder {
val variables: StylePropertyList val variables: StylePropertyList
} }
interface StyleScope : StyleBuilder, StyleHolder {
@ComposeWebInternalApi
fun copyFrom(sb: StyleScope)
}
@Deprecated( @Deprecated(
message = "Renamed to StyleScopeBuilder", message = "Renamed to StyleScopeBuilder",
replaceWith = ReplaceWith("StyleScopeBuilder", "org.jetbrains.compose.web.css.StyleScopeBuilder") replaceWith = ReplaceWith("StyleScopeBuilder", "org.jetbrains.compose.web.css.StyleScopeBuilder")
) )
typealias StyleBuilderImpl = StyleScopeBuilder typealias StyleBuilderImpl = StyleScopeBuilder
@OptIn(ComposeWebInternalApi::class)
@Suppress("EqualsOrHashCode") @Suppress("EqualsOrHashCode")
open class StyleScopeBuilder : StyleScope { open class StyleScopeBuilder : StyleScope, StyleHolder {
override val properties: MutableStylePropertyList = mutableListOf() override val properties: MutableStylePropertyList = mutableListOf()
override val variables: MutableStylePropertyList = mutableListOf() override val variables: MutableStylePropertyList = mutableListOf()
@ -164,7 +164,8 @@ open class StyleScopeBuilder : StyleScope {
} else false } else false
} }
override fun copyFrom(sb: StyleScope) { @ComposeWebInternalApi
internal fun copyFrom(sb: StyleHolder) {
properties.addAll(sb.properties) properties.addAll(sb.properties)
variables.addAll(sb.variables) variables.addAll(sb.variables)
} }

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

@ -127,7 +127,10 @@ fun <TElement : Element> TagElement(
set(attrsScope.classes, DomElementWrapper::updateClasses) set(attrsScope.classes, DomElementWrapper::updateClasses)
set(attrsScope.styleScope, DomElementWrapper::updateStyleDeclarations) set(attrsScope.styleScope, DomElementWrapper::updateStyleDeclarations)
set(attrsScope.collect(), DomElementWrapper::updateAttrs) set(attrsScope.collect(), DomElementWrapper::updateAttrs)
set(attrsScope.collectListeners(), DomElementWrapper::updateEventListeners) set(
attrsScope.eventsListenerScopeBuilder.collectListeners(),
DomElementWrapper::updateEventListeners
)
set(attrsScope.propertyUpdates, DomElementWrapper::updateProperties) set(attrsScope.propertyUpdates, DomElementWrapper::updateProperties)
} }
}, },

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

@ -598,11 +598,7 @@ fun Select(
applyAttrs = { applyAttrs = {
if (multiple) multiple() if (multiple) multiple()
if (attrs != null) { if (attrs != null) {
val selectAttrsBuilder = with(SelectAttrsScope()) { SelectAttrsScope(this).attrs()
attrs()
this
}
copyFrom(selectAttrsBuilder)
} }
}, },
content = content content = content
@ -682,7 +678,7 @@ fun TextArea(
TagElement( TagElement(
elementBuilder = TextArea, elementBuilder = TextArea,
applyAttrs = { applyAttrs = {
val textAreaAttrsBuilder = TextAreaAttrsScope() val textAreaAttrsBuilder = TextAreaAttrsScope(this)
textAreaAttrsBuilder.onInput { textAreaAttrsBuilder.onInput {
// controlled state needs to be restored after every input // controlled state needs to be restored after every input
keyForRestoringControlledState.value = keyForRestoringControlledState.value + 1 keyForRestoringControlledState.value = keyForRestoringControlledState.value + 1
@ -693,8 +689,6 @@ fun TextArea(
if (firstProvidedValueWasNotNull) { if (firstProvidedValueWasNotNull) {
textAreaAttrsBuilder.value(value ?: "") textAreaAttrsBuilder.value(value ?: "")
} }
this.copyFrom(textAreaAttrsBuilder)
}, },
content = { content = {
DisposableEffect(keyForRestoringControlledState.value) { DisposableEffect(keyForRestoringControlledState.value) {
@ -1006,7 +1000,7 @@ fun <K> Input(
TagElement( TagElement(
elementBuilder = Input, elementBuilder = Input,
applyAttrs = { applyAttrs = {
val inputAttrsBuilder = InputAttrsScope(type) val inputAttrsBuilder = InputAttrsScope(type, this)
inputAttrsBuilder.type(type) inputAttrsBuilder.type(type)
inputAttrsBuilder.onInput { inputAttrsBuilder.onInput {
// controlled state needs to be restored after every input // controlled state needs to be restored after every input
@ -1014,8 +1008,6 @@ fun <K> Input(
} }
inputAttrsBuilder.attrs() inputAttrsBuilder.attrs()
this.copyFrom(inputAttrsBuilder)
}, },
content = { content = {
if (type == InputType.Radio) { if (type == InputType.Radio) {

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

@ -164,7 +164,10 @@ class AttributesTests {
assertEquals(attrsScopeCopyFrom.styleScope, copyToAttrsScope.styleScope) assertEquals(attrsScopeCopyFrom.styleScope, copyToAttrsScope.styleScope)
assertEquals(attrsScopeCopyFrom.refEffect, copyToAttrsScope.refEffect) assertEquals(attrsScopeCopyFrom.refEffect, copyToAttrsScope.refEffect)
assertEquals(attrsScopeCopyFrom.propertyUpdates, copyToAttrsScope.propertyUpdates) assertEquals(attrsScopeCopyFrom.propertyUpdates, copyToAttrsScope.propertyUpdates)
assertEquals(attrsScopeCopyFrom.collectListeners(), copyToAttrsScope.collectListeners()) assertEquals(
attrsScopeCopyFrom.eventsListenerScopeBuilder.collectListeners(),
copyToAttrsScope.eventsListenerScopeBuilder.collectListeners()
)
} }
@Test @Test
@ -186,7 +189,7 @@ class AttributesTests {
assertEquals("id1", copyToAttrsScope.attributesMap["id"]) assertEquals("id1", copyToAttrsScope.attributesMap["id"])
assertEquals(StyleScopeBuilder().apply { width(100.px) }, copyToAttrsScope.styleScope) assertEquals(StyleScopeBuilder().apply { width(100.px) }, copyToAttrsScope.styleScope)
val listeners = copyToAttrsScope.collectListeners() val listeners = copyToAttrsScope.eventsListenerScopeBuilder.collectListeners()
assertEquals(1, listeners.size) assertEquals(1, listeners.size)
assertEquals("click", listeners[0].event) assertEquals("click", listeners[0].event)
} }

1
web/settings.gradle.kts

@ -77,7 +77,6 @@ if (extra["compose.web.tests.skip.benchmarks"]!!.toString().toBoolean() != true)
if (extra["compose.web.buildSamples"]!!.toString().toBoolean() == true) { if (extra["compose.web.buildSamples"]!!.toString().toBoolean() == true) {
println("building with examples") println("building with examples")
module(":examples:falling-balls-web", "../examples/falling-balls-web")
module(":examples:compose-web-lp", "../examples/web-landing") module(":examples:compose-web-lp", "../examples/web-landing")
module(":examples:web-compose-bird", "../examples/web-compose-bird") module(":examples:web-compose-bird", "../examples/web-compose-bird")
module(":examples:web-with-react", "../examples/web-with-react") module(":examples:web-with-react", "../examples/web-with-react")

Loading…
Cancel
Save