Browse Source

Web docs updated (#994)

* Minimal documentation for AttrsBuilder

* Minimal documentation for the StyleBuilder

* Add documentation for AttrsBuilder::attr

* Update doc for AtrsBuilder

* Minimal documentation for EventsListenerBuilder

* Documentation for AttrsBuilder::ref (copied from tutorial)

* Documentation for AttrsBuilder::prop

* Add docs for ElementScope and dom specific effects

Co-authored-by: Oleksandr Karpovich <oleksandr.karpovich@jetbrains.com>
pull/996/head
Shagen Ogandzhanian 3 years ago committed by GitHub
parent
commit
b7af8368ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 59
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/AttrsBuilder.kt
  2. 12
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/EventsListenerBuilder.kt
  3. 23
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/css/StyleBuilder.kt
  4. 37
      web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/ElementScope.kt

59
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/AttrsBuilder.kt

@ -7,6 +7,16 @@ import org.jetbrains.compose.web.css.StyleBuilderImpl
import org.w3c.dom.Element import org.w3c.dom.Element
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
/**
* [AttrsBuilder] is a class that is used (as a builder context, that is as AttrsBuilder<T>.() -> Unit)
* 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])
*
* 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() { open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
internal val attributesMap = mutableMapOf<String, String>() internal val attributesMap = mutableMapOf<String, String>()
val styleBuilder = StyleBuilderImpl() val styleBuilder = StyleBuilderImpl()
@ -14,10 +24,26 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
val propertyUpdates = mutableListOf<Pair<(Element, Any) -> Unit, Any>>() val propertyUpdates = mutableListOf<Pair<(Element, Any) -> Unit, Any>>()
var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)? = null var refEffect: (DisposableEffectScope.(TElement) -> DisposableEffectResult)? = null
/**
* [style] add inline CSS-style properties to the element via [StyleBuilder] context
*
* Example:
* ```
* Div({
* style { maxWidth(5.px) }
* })
* ```
*/
fun style(builder: StyleBuilder.() -> Unit) { fun style(builder: StyleBuilder.() -> Unit) {
styleBuilder.apply(builder) styleBuilder.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 id(value: String) = attr(ID, value)
@ -30,15 +56,48 @@ open class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
fun tabIndex(value: Int) = attr(TAB_INDEX, value.toString()) fun tabIndex(value: Int) = attr(TAB_INDEX, value.toString())
fun spellCheck(value: Boolean) = attr(SPELLCHECK, value.toString()) fun spellCheck(value: Boolean) = attr(SPELLCHECK, value.toString())
/**
* [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) { fun ref(effect: DisposableEffectScope.(TElement) -> DisposableEffectResult) {
this.refEffect = effect this.refEffect = effect
} }
/**
* [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): AttrsBuilder<TElement> { fun attr(attr: String, value: String): AttrsBuilder<TElement> {
attributesMap[attr] = value attributesMap[attr] = value
return this return this
} }
/**
* [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") @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) {
propertyUpdates.add((update to value) as Pair<(Element, Any) -> Unit, Any>) propertyUpdates.add((update to value) as Pair<(Element, Any) -> Unit, Any>)

12
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/attributes/EventsListenerBuilder.kt

@ -12,6 +12,12 @@ private typealias SyntheticMouseEventListener = (SyntheticMouseEvent) -> Unit
private typealias SyntheticWheelEventListener = (SyntheticWheelEvent) -> Unit private typealias SyntheticWheelEventListener = (SyntheticWheelEvent) -> Unit
private typealias SyntheticDragEventListener = (SyntheticDragEvent) -> Unit private typealias SyntheticDragEventListener = (SyntheticDragEvent) -> Unit
/**
* [EventsListenerBuilder] is used most often not directly but via [AttrsBuilder].
* 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 { open class EventsListenerBuilder {
protected val listeners = mutableListOf<SyntheticEventListener<*>>() protected val listeners = mutableListOf<SyntheticEventListener<*>>()
@ -184,6 +190,12 @@ open class EventsListenerBuilder {
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
* @param options - as of now this param is always equal to Options.DEFAULT
* @listener - event handler
*/
fun <T : SyntheticEvent<out EventTarget>> addEventListener( fun <T : SyntheticEvent<out EventTarget>> addEventListener(
eventName: String, eventName: String,
options: Options = Options.DEFAULT, options: Options = Options.DEFAULT,

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

@ -9,7 +9,30 @@ package org.jetbrains.compose.web.css
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
/**
* StyleBuilder serves for two main purposes. Passed as a builder context (in [AttrsBuilder]), it
* makes it possible to:
* 1. Add inlined css properties to the element (@see [property])
* 2. Set values to CSS variables (@see [variable])
*/
interface StyleBuilder { interface StyleBuilder {
/**
* 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 value - the value, it can be either String or specialized type like [CSSNumeric] or [CSSColorValue]
*
* Most frequent CSS property values can be set via specialized methods, like [width], [display] etc.
*
* Example:
* ```
* Div({
* style {
* property("some-exotic-css-property", "I am a string value")
* property("some-exotic-css-property-width", 5.px)
* }
* })
* ```
*/
fun property(propertyName: String, value: StylePropertyValue) fun property(propertyName: String, value: StylePropertyValue)
fun variable(variableName: String, value: StylePropertyValue) fun variable(variableName: String, value: StylePropertyValue)

37
web/core/src/jsMain/kotlin/org/jetbrains/compose/web/elements/ElementScope.kt

@ -15,8 +15,27 @@ import org.w3c.dom.HTMLElement
interface DOMScope<out TElement : Element> interface DOMScope<out TElement : Element>
/**
* ElementScope allows adding effects to the Composable representing html element.
* Also see a tutorial: https://github.com/JetBrains/compose-jb/tree/master/tutorials/Web/Using_Effects
*
* Example:
* ```
* Div {
* DisposableRefEffect { htmlDivElement ->
* onDispose {}
* }
* }
* ```
*/
interface ElementScope<out TElement : Element> : DOMScope<TElement> { interface ElementScope<out TElement : Element> : DOMScope<TElement> {
/**
* A side effect of composition that must run for any new unique value of [key]
* and must be reversed or cleaned up if [key] changes or if the DisposableRefEffect leaves the composition.
* [effect] lambda provides a reference to a native element represented by Composable.
* Adding [DisposableEffectScope.onDispose] to [effect] is mandatory.
*/
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun DisposableRefEffect( fun DisposableRefEffect(
@ -24,6 +43,12 @@ interface ElementScope<out TElement : Element> : DOMScope<TElement> {
effect: DisposableEffectScope.(TElement) -> DisposableEffectResult effect: DisposableEffectScope.(TElement) -> DisposableEffectResult
) )
/**
* A side effect of composition that must run once an element enters composition
* and must be reversed or cleaned up if element or the DisposableRefEffect leaves the composition.
* [effect] lambda provides a reference to a native element represented by Composable.
* Adding [DisposableEffectScope.onDispose] to [effect] is mandatory.
*/
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun DisposableRefEffect( fun DisposableRefEffect(
@ -32,10 +57,20 @@ interface ElementScope<out TElement : Element> : DOMScope<TElement> {
DisposableRefEffect(null, effect) DisposableRefEffect(null, effect)
} }
/**
* A side effect of composition that runs on every successful recomposition if [key] changes.
* Also see [SideEffect].
* Same as other effects in [ElementScope], it provides a reference to a native element in [effect] lambda.
*/
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun DomSideEffect(key: Any?, effect: DomEffectScope.(TElement) -> Unit) fun DomSideEffect(key: Any?, effect: DomEffectScope.(TElement) -> Unit)
/**
* A side effect of composition that runs on every successful recomposition.
* Also see [SideEffect].
* Same as other effects in [ElementScope], it provides a reference to a native element in [effect] lambda.
*/
@Composable @Composable
@NonRestartableComposable @NonRestartableComposable
fun DomSideEffect(effect: DomEffectScope.(TElement) -> Unit) fun DomSideEffect(effect: DomEffectScope.(TElement) -> Unit)
@ -102,4 +137,4 @@ private class DomDisposableEffectHolder(
override fun onDispose(disposeEffect: (Element) -> Unit) { override fun onDispose(disposeEffect: (Element) -> Unit) {
onDispose = disposeEffect onDispose = disposeEffect
} }
} }

Loading…
Cancel
Save