From 846b78ffc52623a02c1f025432c143f7533d7e49 Mon Sep 17 00:00:00 2001 From: Nikita Gazarov Date: Thu, 15 Aug 2024 04:35:42 -0700 Subject: [PATCH] New: Link reflected attrs and props when obtained via ReflectedHtmlAttr This is needed to give Laminar enough info to remove a prop by removing its reflected html attribute. There is no standard way to unset a built-in JS prop on html elements. I do not like this approach in SDT very much, but this is what I could muster without refactoring lots of things. --- .../generators/PropsTraitGenerator.scala | 40 +++-- .../com/raquo/domtypes/common/AttrDef.scala | 4 + .../com/raquo/domtypes/common/PropDef.scala | 30 ++-- .../common/ReflectedHtmlAttrDef.scala | 18 +- .../domtypes/defs/attrs/AriaAttrDefs.scala | 36 ++++ .../domtypes/defs/attrs/HtmlAttrDefs.scala | 16 ++ .../domtypes/defs/attrs/SvgAttrDefs.scala | 170 ++++++++++++++++++ .../raquo/domtypes/defs/props/PropDefs.scala | 4 + 8 files changed, 284 insertions(+), 34 deletions(-) diff --git a/shared/src/main/scala/com/raquo/domtypes/codegen/generators/PropsTraitGenerator.scala b/shared/src/main/scala/com/raquo/domtypes/codegen/generators/PropsTraitGenerator.scala index a6da0002..3fa0eea6 100644 --- a/shared/src/main/scala/com/raquo/domtypes/codegen/generators/PropsTraitGenerator.scala +++ b/shared/src/main/scala/com/raquo/domtypes/codegen/generators/PropsTraitGenerator.scala @@ -79,25 +79,27 @@ class PropsTraitGenerator( line() line() - distinctImplNames().foreach { implName => - line( - InlineProtectedDef.codeStr, - " ", - implName, - s"($keyImplNameArgName: String)", - ": ", - keyKind, - "[", - scalaValueTypeByImplName(implName), - ", ", - domValueTypeByImplName(implName), - "]", - " = ", - baseImplName, - s"($keyImplNameArgName, ${transformCodecName(codecByImplName(implName))})", - ) - line() - } + distinctImplNames().foreach(printImplDef) + } + + protected def printImplDef(implName: String): Unit = { + line( + InlineProtectedDef.codeStr, + " ", + implName, + s"($keyImplNameArgName: String)", + ": ", + keyKind, + "[", + scalaValueTypeByImplName(implName), + ", ", + domValueTypeByImplName(implName), + "]", + " = ", + baseImplName, + s"($keyImplNameArgName, ${transformCodecName(codecByImplName(implName))})", + ) + line() } } diff --git a/shared/src/main/scala/com/raquo/domtypes/common/AttrDef.scala b/shared/src/main/scala/com/raquo/domtypes/common/AttrDef.scala index f0bcf91f..40b86b10 100644 --- a/shared/src/main/scala/com/raquo/domtypes/common/AttrDef.scala +++ b/shared/src/main/scala/com/raquo/domtypes/common/AttrDef.scala @@ -17,6 +17,9 @@ package com.raquo.domtypes.common * @param scalaValueType - Type of values you can write to this attribute in Scala * Note: in the DOM, the attribute's value is always `String`. * @param codec - Codec needed to convert between `scalaValueType` and String + * @param reflectedProp - If the attribute is reflected, this is the corresponding property. + * Note that the linked prop instance will NOT have this attr + * in its `reflectedAttr` field to prevent a cyclic reference. * @param commentLines - Scaladoc comment lines for this key * @param docUrls - Scaladoc documentation URLs for this key */ @@ -28,6 +31,7 @@ case class AttrDef( namespace: Option[String], scalaValueType: String, codec: String, + reflectedProp: Option[PropDef], override val commentLines: List[String], override val docUrls: List[String] ) extends KeyDef { diff --git a/shared/src/main/scala/com/raquo/domtypes/common/PropDef.scala b/shared/src/main/scala/com/raquo/domtypes/common/PropDef.scala index a63f1464..f1ff8036 100644 --- a/shared/src/main/scala/com/raquo/domtypes/common/PropDef.scala +++ b/shared/src/main/scala/com/raquo/domtypes/common/PropDef.scala @@ -3,19 +3,22 @@ package com.raquo.domtypes.common /** * This type represents an HTML element property. * - * @param scalaName - Suggested name of this prop in Scala. - * The name is chosen to - * - match Scala naming style (e.g. camelCase) - * - avoid name collisions between different types of keys - * (e.g. `title` tag vs `title` attribute) - * - avoid using up popular names for unpopular keys - * @param scalaAliases - Aliases that should be defined linking to the main `scalaName` - * @param domName - Native attribute name in the DOM. - * @param scalaValueType - Type of values you can write to this attribute in Scala - * @param domValueType - Type of values that this property holds in the DOM - * @param codec - Codec needed to convert between `scalaValueType` and `domValueType` - * @param commentLines - Scaladoc comment lines for this key - * @param docUrls - Scaladoc documentation URLs for this key + * @param scalaName - Suggested name of this prop in Scala. + * The name is chosen to + * - match Scala naming style (e.g. camelCase) + * - avoid name collisions between different types of keys + * (e.g. `title` tag vs `title` attribute) + * - avoid using up popular names for unpopular keys + * @param scalaAliases - Aliases that should be defined linking to the main `scalaName` + * @param domName - Native property name in the DOM. + * @param scalaValueType - Type of values you can write to this attribute in Scala + * @param domValueType - Type of values that this property holds in the DOM + * @param codec - Codec needed to convert between `scalaValueType` and `domValueType` + * @param reflectedAttr - If the property is reflected, this is the corresponding attr. + * Note that the linked attr instance will NOT have this prop + * in its `reflectedProp` field to prevent a cyclic reference. + * @param commentLines - Scaladoc comment lines for this key + * @param docUrls - Scaladoc documentation URLs for this key */ case class PropDef( scalaName: String, @@ -24,6 +27,7 @@ case class PropDef( scalaValueType: String, domValueType: String, codec: String, + reflectedAttr: Option[AttrDef], override val commentLines: List[String], override val docUrls: List[String] ) extends KeyDef diff --git a/shared/src/main/scala/com/raquo/domtypes/common/ReflectedHtmlAttrDef.scala b/shared/src/main/scala/com/raquo/domtypes/common/ReflectedHtmlAttrDef.scala index 20f197e2..0760a2df 100644 --- a/shared/src/main/scala/com/raquo/domtypes/common/ReflectedHtmlAttrDef.scala +++ b/shared/src/main/scala/com/raquo/domtypes/common/ReflectedHtmlAttrDef.scala @@ -40,24 +40,38 @@ case class ReflectedHtmlAttrDef( override val docUrls: List[String] ) extends KeyDef { - def toPropDef: PropDef = PropDef( + private lazy val _rawPropDef = PropDef( scalaName = scalaName, domName = domPropName, scalaValueType = scalaValueType, domValueType = domPropValueType, codec = propCodec, + reflectedAttr = None, commentLines = commentLines, docUrls = docUrls ) - def toAttrDef: AttrDef = AttrDef( + private lazy val _rawAttrDef = AttrDef( tagType = HtmlTagType, scalaName = scalaName, domName = domAttrName, namespace = None, scalaValueType = scalaValueType, codec = attrCodec, + reflectedProp = None, commentLines = commentLines, docUrls = docUrls ) + + lazy val toPropDef: PropDef = { + _rawPropDef.copy( + reflectedAttr = Some(_rawAttrDef) + ) + } + + lazy val toAttrDef: AttrDef = { + _rawAttrDef.copy( + reflectedProp = Some(_rawPropDef) + ) + } } diff --git a/shared/src/main/scala/com/raquo/domtypes/defs/attrs/AriaAttrDefs.scala b/shared/src/main/scala/com/raquo/domtypes/defs/attrs/AriaAttrDefs.scala index 214d6cb0..505d91f3 100644 --- a/shared/src/main/scala/com/raquo/domtypes/defs/attrs/AriaAttrDefs.scala +++ b/shared/src/main/scala/com/raquo/domtypes/defs/attrs/AriaAttrDefs.scala @@ -20,6 +20,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Identifies the currently active descendant of a composite widget.", ), @@ -35,6 +36,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates whether assistive technologies will present all, or only parts of, the", "changed region based on the change notifications defined by the aria-relevant", @@ -52,6 +54,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates whether user input completion suggestions are provided.", "", @@ -69,6 +72,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates whether an element, and its subtree, are currently being updated.", ), @@ -84,6 +88,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates the current \"checked\" state of checkboxes, radio buttons, and other", "widgets. See related [[pressed]] and [[selected]].", @@ -103,6 +108,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Identifies the element (or elements) whose contents or presence are controlled", "by the current element. See related [[owns]].", @@ -119,6 +125,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates the element that represents the current item within a container", "or set of related elements.", @@ -138,6 +145,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Identifies the element (or elements) that describes the object.", "See related [[labelledBy]].", @@ -154,6 +162,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates that the element is perceivable but disabled, so it is not editable", "or otherwise operable. See related [[hidden]] and [[readOnly]].", @@ -170,6 +179,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates what functions can be performed when the dragged object is released", "on the drop target. This allows assistive technologies to convey the possible", @@ -192,6 +202,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates whether the element, or another grouping element it controls, is", "currently expanded or collapsed.", @@ -208,6 +219,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Identifies the next element (or elements) in an alternate reading order of", "content which, at the user's discretion, allows assistive technology to", @@ -225,6 +237,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates an element's \"grabbed\" state in a drag-and-drop operation.", ), @@ -240,6 +253,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates that the element has a popup context menu or sub-level menu.", ), @@ -255,6 +269,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates that the element and all of its descendants are not visible or", "perceivable to any user as implemented by the author.", @@ -272,6 +287,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates the entered value does not conform to the format expected by the", "application.", @@ -290,6 +306,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Defines a string value that labels the current element.", "See related [[labelledBy]].", @@ -306,6 +323,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Identifies the element (or elements) that labels the current element.", "See related [[label]] and [[describedBy]].", @@ -322,6 +340,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Int", codec = "IntAsString", + reflectedProp = None, commentLines = List( "Defines the hierarchical level of an element within a structure.", ), @@ -337,6 +356,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates that an element will be updated, and describes the types of updates the", "user agents, assistive technologies, and user can expect from the live region.", @@ -355,6 +375,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates whether a text box accepts multiple lines of input or only a single line.", ), @@ -370,6 +391,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates that the user may select more than one item from the current selectable descendants.", ), @@ -385,6 +407,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates whether the element and orientation is horizontal or vertical.", "", @@ -402,6 +425,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Identifies an element (or elements) in order to define a visual, functional, or", "contextual parent/child relationship between DOM elements where the DOM hierarchy", @@ -419,6 +443,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Int", codec = "IntAsString", + reflectedProp = None, commentLines = List( "Defines an element's number or position in the current set of listitems or treeitems.", "Not required if all elements in the set are present in the DOM. See related [[setSize]].", @@ -435,6 +460,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates the current \"pressed\" state of toggle buttons. See related [[checked]] and [[selected]].", "", @@ -453,6 +479,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates that the element is not editable, but is otherwise operable. See related [[disabled]].", ), @@ -468,6 +495,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates what user agent change notifications (additions, removals, etc.)", "assistive technologies will receive within a live region. See related [[atomic]].", @@ -486,6 +514,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates that user input is required on the element before a form may be submitted.", ), @@ -501,6 +530,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates the current \"selected\" state of various widgets.", "See related [[checked]] and [[pressed]].", @@ -517,6 +547,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Int", codec = "IntAsString", + reflectedProp = None, commentLines = List( "Defines the number of items in the current set of listitems or treeitems.", "Not required if all elements in the set are present in the DOM.", @@ -534,6 +565,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Indicates if items in a table or grid are sorted in ascending or descending order.", "", @@ -551,6 +583,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Double", codec = "DoubleAsString", + reflectedProp = None, commentLines = List( "Defines the maximum allowed value for a range widget.", ), @@ -566,6 +599,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Double", codec = "DoubleAsString", + reflectedProp = None, commentLines = List( "Defines the minimum allowed value for a range widget.", ), @@ -581,6 +615,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "Double", codec = "DoubleAsString", + reflectedProp = None, commentLines = List( "Defines the current value for a range widget. See related [[valueText]].", ), @@ -596,6 +631,7 @@ object AriaAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Defines the human readable text alternative of aria-valuenow for a range widget.", ), diff --git a/shared/src/main/scala/com/raquo/domtypes/defs/attrs/HtmlAttrDefs.scala b/shared/src/main/scala/com/raquo/domtypes/defs/attrs/HtmlAttrDefs.scala index d270158c..8cf90c0a 100644 --- a/shared/src/main/scala/com/raquo/domtypes/defs/attrs/HtmlAttrDefs.scala +++ b/shared/src/main/scala/com/raquo/domtypes/defs/attrs/HtmlAttrDefs.scala @@ -14,6 +14,7 @@ object HtmlAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Declares the character encoding of the page or script. Used on meta and", "script elements.", @@ -31,6 +32,7 @@ object HtmlAttrDefs { namespace = None, scalaValueType = "Boolean", codec = "BooleanAsTrueFalseString", + reflectedProp = None, commentLines = List( "Indicates whether the element should be editable by the user.", "If so, the browser modifies its widget to allow editing.", @@ -47,6 +49,7 @@ object HtmlAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Specifies a context menu for an element by its element id.", "The context menu appears when a user right-clicks on the element", @@ -63,6 +66,7 @@ object HtmlAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "Specifies whether the dragged data is copied, moved, or linked, when dropped", "Acceptable values: `copy` | `move` | `link`", @@ -82,6 +86,7 @@ object HtmlAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "The `formaction` attribute provides the URL that will process the input control ", "when the form is submitted and overrides the default `action` attribute of the ", @@ -103,6 +108,7 @@ object HtmlAttrDefs { namespace = None, scalaValueType = "String", codec = "StringAsIs", + reflectedProp = None, commentLines = List( "The form attribute specifies an ID of the form an `` element belongs to.", ), @@ -116,6 +122,7 @@ object HtmlAttrDefs { namespace = None, scalaValueType = "Int", codec = "IntAsString", + reflectedProp = None, commentLines = List( "The `height` attribute specifies the pixel height of the following elements:", "`, ,