Browse Source

Don't allow to assign null value in attr (#780)

* Remove atrributes completely and reapply everything

Rather than keeping map with attributes applied

* Don't allow nullable signatures while calling attr
DOM_BUILDER_IN_ONE_PLACE
Shagen Ogandzhanian 3 years ago committed by GitHub
parent
commit
4529645a75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      examples/web_landing/src/jsMain/kotlin/com/sample/content/CodeSamplesSwitcher.kt
  2. 2
      web/benchmark-core/src/jsMain/kotlin/com/sample/content/CodeSamplesSwitcher.kt
  3. 14
      web/core/src/jsMain/kotlin/androidx/compose/web/DomApplier.kt
  4. 106
      web/core/src/jsMain/kotlin/androidx/compose/web/attributes/Attrs.kt
  5. 7
      web/core/src/jsMain/kotlin/androidx/compose/web/attributes/AttrsBuilder.kt
  6. 6
      web/core/src/jsMain/kotlin/androidx/compose/web/elements/Elements.kt
  7. 4
      web/core/src/jsTest/kotlin/elements/AttributesTests.kt
  8. 4
      web/integration-core/src/jsMain/kotlin/androidx/compose/web/sample/tests/InputsTests.kt

2
examples/web_landing/src/jsMain/kotlin/com/sample/content/CodeSamplesSwitcher.kt

@ -80,7 +80,7 @@ fun CodeSampleSwitcher(count: Int, current: Int, onSelect: (Int) -> Unit) {
Input(type = InputType.Radio, value = "snippet$ix", attrs = { Input(type = InputType.Radio, value = "snippet$ix", attrs = {
name("code-snippet") name("code-snippet")
id("snippet$ix") id("snippet$ix")
if (current == ix) checked(true) if (current == ix) checked()
onRadioInput { onSelect(ix) } onRadioInput { onSelect(ix) }
}) })
Label(forId = "snippet$ix") { Text("${ix + 1}") } Label(forId = "snippet$ix") { Text("${ix + 1}") }

2
web/benchmark-core/src/jsMain/kotlin/com/sample/content/CodeSamplesSwitcher.kt

@ -74,7 +74,7 @@ fun CodeSampleSwitcher(count: Int, current: Int, onSelect: (Int) -> Unit) {
Input(type = InputType.Radio, value = "snippet$ix", attrs = { Input(type = InputType.Radio, value = "snippet$ix", attrs = {
name("code-snippet") name("code-snippet")
id("snippet$ix") id("snippet$ix")
if (current == ix) checked(true) if (current == ix) checked()
onRadioInput { onSelect(ix) } onRadioInput { onSelect(ix) }
}) })
Label(forId = "snippet$ix") { Text("${ix + 1}") } Label(forId = "snippet$ix") { Text("${ix + 1}") }

14
web/core/src/jsMain/kotlin/androidx/compose/web/DomApplier.kt

@ -89,15 +89,13 @@ open class DomNodeWrapper(open val node: Node) {
class DomElementWrapper(override val node: HTMLElement): DomNodeWrapper(node) { class DomElementWrapper(override val node: HTMLElement): DomNodeWrapper(node) {
private var currentAttrs = emptyMap<String, String?>() fun updateAttrs(attrs: Map<String, String>) {
while (node.attributes.length > 0) {
fun updateAttrs(attrs: Map<String, String?>) { node.removeAttributeNode(node.attributes[0]!!)
currentAttrs.forEach {
node.removeAttribute(it.key)
} }
currentAttrs = attrs
currentAttrs.forEach { attrs.forEach {
if (it.value != null) node.setAttribute(it.key, it.value ?: "") node.setAttribute(it.key, it.value)
} }
} }

106
web/core/src/jsMain/kotlin/androidx/compose/web/attributes/Attrs.kt

@ -13,7 +13,7 @@ import org.w3c.dom.HTMLTableCellElement
import org.w3c.dom.HTMLTableColElement import org.w3c.dom.HTMLTableColElement
import org.w3c.dom.HTMLTextAreaElement import org.w3c.dom.HTMLTextAreaElement
fun AttrsBuilder<HTMLAnchorElement>.href(value: String?) = fun AttrsBuilder<HTMLAnchorElement>.href(value: String) =
attr("href", value) attr("href", value)
fun AttrsBuilder<HTMLAnchorElement>.target(value: ATarget = ATarget.Self) = fun AttrsBuilder<HTMLAnchorElement>.target(value: ATarget = ATarget.Self) =
@ -36,11 +36,11 @@ fun AttrsBuilder<HTMLAnchorElement>.download(value: String = "") =
/* Button attributes */ /* Button attributes */
fun AttrsBuilder<HTMLButtonElement>.autoFocus(value: Boolean = true) = fun AttrsBuilder<HTMLButtonElement>.autoFocus() =
attr("autofocus", if (value) "" else null) attr("autofocus", "")
fun AttrsBuilder<HTMLButtonElement>.disabled(value: Boolean = true) = fun AttrsBuilder<HTMLButtonElement>.disabled() =
attr("disabled", if (value) "" else null) attr("disabled", "")
fun AttrsBuilder<HTMLButtonElement>.form(formId: String) = fun AttrsBuilder<HTMLButtonElement>.form(formId: String) =
attr("form", formId) attr("form", formId)
@ -54,8 +54,8 @@ fun AttrsBuilder<HTMLButtonElement>.formEncType(value: ButtonFormEncType) =
fun AttrsBuilder<HTMLButtonElement>.formMethod(value: ButtonFormMethod) = fun AttrsBuilder<HTMLButtonElement>.formMethod(value: ButtonFormMethod) =
attr("formmethod", value.methodStr) attr("formmethod", value.methodStr)
fun AttrsBuilder<HTMLButtonElement>.formNoValidate(value: Boolean = true) = fun AttrsBuilder<HTMLButtonElement>.formNoValidate() =
attr("formnovalidate", if (value) "" else null) attr("formnovalidate", "")
fun AttrsBuilder<HTMLButtonElement>.formTarget(value: ButtonFormTarget) = fun AttrsBuilder<HTMLButtonElement>.formTarget(value: ButtonFormTarget) =
attr("formtarget", value.targetStr) attr("formtarget", value.targetStr)
@ -77,8 +77,8 @@ fun AttrsBuilder<HTMLFormElement>.action(value: String) =
fun AttrsBuilder<HTMLFormElement>.acceptCharset(value: String) = fun AttrsBuilder<HTMLFormElement>.acceptCharset(value: String) =
attr("accept-charset", value) attr("accept-charset", value)
fun AttrsBuilder<HTMLFormElement>.autoComplete(value: Boolean) = fun AttrsBuilder<HTMLFormElement>.autoComplete() =
attr("autocomplete", if (value) "" else null) attr("autocomplete", "")
fun AttrsBuilder<HTMLFormElement>.encType(value: FormEncType) = fun AttrsBuilder<HTMLFormElement>.encType(value: FormEncType) =
attr("enctype", value.typeStr) attr("enctype", value.typeStr)
@ -86,8 +86,8 @@ fun AttrsBuilder<HTMLFormElement>.encType(value: FormEncType) =
fun AttrsBuilder<HTMLFormElement>.method(value: FormMethod) = fun AttrsBuilder<HTMLFormElement>.method(value: FormMethod) =
attr("method", value.methodStr) attr("method", value.methodStr)
fun AttrsBuilder<HTMLFormElement>.noValidate(value: Boolean = true) = fun AttrsBuilder<HTMLFormElement>.noValidate() =
attr("novalidate", if (value) "" else null) attr("novalidate", "")
fun AttrsBuilder<HTMLFormElement>.target(value: FormTarget) = fun AttrsBuilder<HTMLFormElement>.target(value: FormTarget) =
attr("target", value.targetStr) attr("target", value.targetStr)
@ -103,23 +103,23 @@ fun AttrsBuilder<HTMLInputElement>.accept(value: String) =
fun AttrsBuilder<HTMLInputElement>.alt(value: String) = fun AttrsBuilder<HTMLInputElement>.alt(value: String) =
attr("alt", value) // type: image only attr("alt", value) // type: image only
fun AttrsBuilder<HTMLInputElement>.autoComplete(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.autoComplete() =
attr("autocomplete", if (value) "" else null) attr("autocomplete", "")
fun AttrsBuilder<HTMLInputElement>.autoFocus(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.autoFocus() =
attr("autofocus", if (value) "" else null) attr("autofocus", "")
fun AttrsBuilder<HTMLInputElement>.capture(value: String) = fun AttrsBuilder<HTMLInputElement>.capture(value: String) =
attr("capture", value) // type: file only attr("capture", value) // type: file only
fun AttrsBuilder<HTMLInputElement>.checked(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.checked() =
attr("checked", if (value) "" else null) // radio, checkbox attr("checked", "") // radio, checkbox
fun AttrsBuilder<HTMLInputElement>.dirName(value: String) = fun AttrsBuilder<HTMLInputElement>.dirName(value: String) =
attr("dirname", value) // text, search attr("dirname", value) // text, search
fun AttrsBuilder<HTMLInputElement>.disabled(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.disabled() =
attr("disabled", if (value) "" else null) attr("disabled", "")
fun AttrsBuilder<HTMLInputElement>.form(id: String) = fun AttrsBuilder<HTMLInputElement>.form(id: String) =
attr("form", id) attr("form", id)
@ -133,8 +133,8 @@ fun AttrsBuilder<HTMLInputElement>.formEncType(value: InputFormEncType) =
fun AttrsBuilder<HTMLInputElement>.formMethod(value: InputFormMethod) = fun AttrsBuilder<HTMLInputElement>.formMethod(value: InputFormMethod) =
attr("formmethod", value.methodStr) attr("formmethod", value.methodStr)
fun AttrsBuilder<HTMLInputElement>.formNoValidate(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.formNoValidate() =
attr("formnovalidate", if (value) "" else null) attr("formnovalidate", "")
fun AttrsBuilder<HTMLInputElement>.formTarget(value: InputFormTarget) = fun AttrsBuilder<HTMLInputElement>.formTarget(value: InputFormTarget) =
attr("formtarget", value.targetStr) attr("formtarget", value.targetStr)
@ -160,8 +160,8 @@ fun AttrsBuilder<HTMLInputElement>.min(value: String) =
fun AttrsBuilder<HTMLInputElement>.minLength(value: Int) = fun AttrsBuilder<HTMLInputElement>.minLength(value: Int) =
attr("minlength", value.toString()) attr("minlength", value.toString())
fun AttrsBuilder<HTMLInputElement>.multiple(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.multiple() =
attr("multiple", if (value) "" else null) attr("multiple", "")
fun AttrsBuilder<HTMLInputElement>.name(value: String) = fun AttrsBuilder<HTMLInputElement>.name(value: String) =
attr("name", value) attr("name", value)
@ -172,8 +172,8 @@ fun AttrsBuilder<HTMLInputElement>.pattern(value: String) =
fun AttrsBuilder<HTMLInputElement>.placeholder(value: String) = fun AttrsBuilder<HTMLInputElement>.placeholder(value: String) =
attr("placeholder", value) attr("placeholder", value)
fun AttrsBuilder<HTMLInputElement>.readOnly(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.readOnly() =
attr("readonly", if (value) "" else null) attr("readonly", "")
fun AttrsBuilder<HTMLInputElement>.required(value: Boolean = true) = fun AttrsBuilder<HTMLInputElement>.required(value: Boolean = true) =
attr("required", value.toString()) attr("required", value.toString())
@ -182,7 +182,7 @@ fun AttrsBuilder<HTMLInputElement>.size(value: Int) =
attr("size", value.toString()) attr("size", value.toString())
fun AttrsBuilder<HTMLInputElement>.src(value: String) = fun AttrsBuilder<HTMLInputElement>.src(value: String) =
attr("src", value.toString()) // image only attr("src", value) // image only
fun AttrsBuilder<HTMLInputElement>.step(value: Int) = fun AttrsBuilder<HTMLInputElement>.step(value: Int) =
attr("step", value.toString()) // numeric types only attr("step", value.toString()) // numeric types only
@ -200,11 +200,11 @@ fun AttrsBuilder<HTMLInputElement>.value(value: String): AttrsBuilder<HTMLInputE
fun AttrsBuilder<HTMLOptionElement>.value(value: String) = fun AttrsBuilder<HTMLOptionElement>.value(value: String) =
attr("value", value) attr("value", value)
fun AttrsBuilder<HTMLOptionElement>.disabled(value: Boolean = true) = fun AttrsBuilder<HTMLOptionElement>.disabled() =
attr("disabled", if (value) "" else null) attr("disabled", "")
fun AttrsBuilder<HTMLOptionElement>.selected(value: Boolean = true) = fun AttrsBuilder<HTMLOptionElement>.selected() =
attr("selected", if (value) "" else null) attr("selected", "")
fun AttrsBuilder<HTMLOptionElement>.label(value: String) = fun AttrsBuilder<HTMLOptionElement>.label(value: String) =
attr("label", value) attr("label", value)
@ -214,23 +214,23 @@ fun AttrsBuilder<HTMLOptionElement>.label(value: String) =
fun AttrsBuilder<HTMLSelectElement>.autocomplete(value: String) = fun AttrsBuilder<HTMLSelectElement>.autocomplete(value: String) =
attr("autocomplete", value) attr("autocomplete", value)
fun AttrsBuilder<HTMLSelectElement>.autofocus(value: Boolean = true) = fun AttrsBuilder<HTMLSelectElement>.autofocus() =
attr("autofocus", if (value) "" else null) attr("autofocus", "")
fun AttrsBuilder<HTMLSelectElement>.disabled(value: Boolean = true) = fun AttrsBuilder<HTMLSelectElement>.disabled() =
attr("disabled", if (value) "" else null) attr("disabled", "")
fun AttrsBuilder<HTMLSelectElement>.form(formId: String) = fun AttrsBuilder<HTMLSelectElement>.form(formId: String) =
attr("form", formId) attr("form", formId)
fun AttrsBuilder<HTMLSelectElement>.multiple(value: Boolean = true) = fun AttrsBuilder<HTMLSelectElement>.multiple() =
attr("multiple", if (value) "" else null) attr("multiple", "")
fun AttrsBuilder<HTMLSelectElement>.name(value: String) = fun AttrsBuilder<HTMLSelectElement>.name(value: String) =
attr("name", value) attr("name", value)
fun AttrsBuilder<HTMLSelectElement>.required(value: Boolean = true) = fun AttrsBuilder<HTMLSelectElement>.required() =
attr("required", if (value) "" else null) attr("required", "")
fun AttrsBuilder<HTMLSelectElement>.size(numberOfRows: Int) = fun AttrsBuilder<HTMLSelectElement>.size(numberOfRows: Int) =
attr("size", numberOfRows.toString()) attr("size", numberOfRows.toString())
@ -240,22 +240,22 @@ fun AttrsBuilder<HTMLSelectElement>.size(numberOfRows: Int) =
fun AttrsBuilder<HTMLOptGroupElement>.label(value: String) = fun AttrsBuilder<HTMLOptGroupElement>.label(value: String) =
attr("label", value) attr("label", value)
fun AttrsBuilder<HTMLOptGroupElement>.disabled(value: Boolean = true) = fun AttrsBuilder<HTMLOptGroupElement>.disabled() =
attr("disabled", if (value) "" else null) attr("disabled", "")
/* TextArea attributes */ /* TextArea attributes */
fun AttrsBuilder<HTMLTextAreaElement>.autoComplete(value: Boolean = true) = fun AttrsBuilder<HTMLTextAreaElement>.autoComplete(value: Boolean = true) =
attr("autocomplete", if (value) "on" else "off") attr("autocomplete", if (value) "on" else "off")
fun AttrsBuilder<HTMLTextAreaElement>.autoFocus(value: Boolean = true) = fun AttrsBuilder<HTMLTextAreaElement>.autoFocus() =
attr("autofocus", if (value) "" else null) attr("autofocus", "")
fun AttrsBuilder<HTMLTextAreaElement>.cols(value: Int) = fun AttrsBuilder<HTMLTextAreaElement>.cols(value: Int) =
attr("cols", value.toString()) attr("cols", value.toString())
fun AttrsBuilder<HTMLTextAreaElement>.disabled(value: Boolean = true) = fun AttrsBuilder<HTMLTextAreaElement>.disabled() =
attr("disabled", if (value) "" else null) attr("disabled", "")
fun AttrsBuilder<HTMLTextAreaElement>.form(formId: String) = fun AttrsBuilder<HTMLTextAreaElement>.form(formId: String) =
attr("form", formId) attr("form", formId)
@ -272,11 +272,11 @@ fun AttrsBuilder<HTMLTextAreaElement>.name(value: String) =
fun AttrsBuilder<HTMLTextAreaElement>.placeholder(value: String) = fun AttrsBuilder<HTMLTextAreaElement>.placeholder(value: String) =
attr("placeholder", value) attr("placeholder", value)
fun AttrsBuilder<HTMLTextAreaElement>.readOnly(value: Boolean = true) = fun AttrsBuilder<HTMLTextAreaElement>.readOnly() =
attr("readonly", if (value) "" else null) attr("readonly", "")
fun AttrsBuilder<HTMLTextAreaElement>.required(value: Boolean = true) = fun AttrsBuilder<HTMLTextAreaElement>.required() =
attr("required", if (value) "" else null) attr("required", "")
fun AttrsBuilder<HTMLTextAreaElement>.rows(value: Int) = fun AttrsBuilder<HTMLTextAreaElement>.rows(value: Int) =
attr("rows", value.toString()) attr("rows", value.toString())
@ -291,10 +291,10 @@ fun AttrsBuilder<HTMLTextAreaElement>.value(value: String): AttrsBuilder<HTMLTex
/* Img attributes */ /* Img attributes */
fun AttrsBuilder<HTMLImageElement>.src(value: String?): AttrsBuilder<HTMLImageElement> = fun AttrsBuilder<HTMLImageElement>.src(value: String): AttrsBuilder<HTMLImageElement> =
attr("src", value) attr("src", value)
fun AttrsBuilder<HTMLImageElement>.alt(value: String?): AttrsBuilder<HTMLImageElement> = fun AttrsBuilder<HTMLImageElement>.alt(value: String): AttrsBuilder<HTMLImageElement> =
attr("alt", value) attr("alt", value)
private val setInputValue: (HTMLInputElement, String) -> Unit = { e, v -> private val setInputValue: (HTMLInputElement, String) -> Unit = { e, v ->
@ -302,15 +302,15 @@ private val setInputValue: (HTMLInputElement, String) -> Unit = { e, v ->
} }
/* Img attributes */ /* Img attributes */
fun AttrsBuilder<HTMLLabelElement>.forId(value: String?): AttrsBuilder<HTMLLabelElement> = fun AttrsBuilder<HTMLLabelElement>.forId(value: String): AttrsBuilder<HTMLLabelElement> =
attr("for", value) attr("for", value)
/* Table attributes */ /* Table attributes */
fun AttrsBuilder<HTMLTableColElement>.span(value: Int): AttrsBuilder<HTMLTableColElement> = fun AttrsBuilder<HTMLTableColElement>.span(value: Int): AttrsBuilder<HTMLTableColElement> =
attr("span", value.toString()) attr("span", value.toString())
fun AttrsBuilder<HTMLTableCellElement>.scope(value: Scope?): AttrsBuilder<HTMLTableCellElement> = fun AttrsBuilder<HTMLTableCellElement>.scope(value: Scope): AttrsBuilder<HTMLTableCellElement> =
attr("scope", value?.str) attr("scope", value.str)
fun AttrsBuilder<HTMLTableCellElement>.colspan(value: Int): AttrsBuilder<HTMLTableCellElement> = fun AttrsBuilder<HTMLTableCellElement>.colspan(value: Int): AttrsBuilder<HTMLTableCellElement> =
attr("colspan", value.toString()) attr("colspan", value.toString())

7
web/core/src/jsMain/kotlin/androidx/compose/web/attributes/AttrsBuilder.kt

@ -34,13 +34,8 @@ class AttrsBuilder<TElement : Element> : EventsListenerBuilder() {
this.refEffect = effect this.refEffect = effect
} }
fun attr(attr: String, value: String?): AttrsBuilder<TElement> { fun attr(attr: String, value: String): AttrsBuilder<TElement> {
if (value == null) {
attributesMap.remove(attr)
} else {
attributesMap[attr] = value attributesMap[attr] = value
}
return this return this
} }

6
web/core/src/jsMain/kotlin/androidx/compose/web/elements/Elements.kt

@ -347,7 +347,9 @@ fun A(
TagElement( TagElement(
elementBuilder = ElementBuilder.A, elementBuilder = ElementBuilder.A,
applyAttrs = { applyAttrs = {
href(href) if (href != null) {
this.href(href)
}
attrs() attrs()
}, },
content = content content = content
@ -645,7 +647,9 @@ fun Label(
TagElement( TagElement(
elementBuilder = ElementBuilder.Label, elementBuilder = ElementBuilder.Label,
applyAttrs = { applyAttrs = {
if (forId != null) {
forId(forId) forId(forId)
}
attrs() attrs()
}, },
content = content content = content

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

@ -68,7 +68,9 @@ class AttributesTests {
composition { composition {
Button( Button(
{ {
disabled(disabled) if (disabled) {
disabled()
}
} }
) {} ) {}
} }

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

@ -49,7 +49,9 @@ class InputsTests {
type = InputType.Checkbox, type = InputType.Checkbox,
attrs = { attrs = {
id("checkbox") id("checkbox")
checked(checked) if (checked) {
checked()
}
onCheckboxInput { onCheckboxInput {
checked = !checked checked = !checked
} }

Loading…
Cancel
Save