A boilerplate-free way to make React components in the wild usable in scalajs-react apps. Write an object for each component, use one of the four provided macros, and start using it in a type-safe manner in scalajs-react apps.
For example, to create a component corresponding to react-tagsinput, define a class as follows:
object TagsInput extends ReactBridgeComponent { def apply(defaultValue: js.UndefOr[Seq[String]] = js.undefined, value: js.UndefOr[Seq[String]] = js.undefined, placeholder: js.UndefOr[String] = js.undefined, onChange: js.UndefOr[js.Array[String] => Callback] = js.undefined, validate: js.UndefOr[String => CallbackTo[Boolean]] = js.undefined, transform: js.UndefOr[String => CallbackTo[String]] = js.undefined): WithPropsNoChildren = autoNoChildren }
Then use it in a scalajs-react app the same way as any other component.
div( TagsInput(value = Seq("foo","bar"), onChange = printSequence _) )
If you want to pass DOM attributes as well as React special attributes such as "key" as additional properties, you can easily do so as follows:
div( TagsInput(value = Seq("foo","bar"), onChange = printSequence _)(className := "tags", key := "key-1") )
Finally, while TagsInput
doesn't allow children (as signified by the return type of the method), if it were to, you could pass children as follows:
div( TagsInput(value = Seq("foo","bar"), onChange = printSequence _)(className := "tags", key := "key-1")( "child1", div(className := "some-div")( span(className: "some-span")("content") ) ) )
Add the following dependency to your scalajs-react project:
libraryDependencies += "com.payalabs" %%% "scalajs-react-bridge" % "0.8.4"
To use the latest snapshot version
resolvers += Resolver.sonatypeRepo("snapshots")
libraryDependencies += "com.payalabs" %%% "scalajs-react-bridge" % "0.8.5-SNAPSHOT"
The core logic of bridging the JS React component to scala-react is implemented using the ReactBridgeComponent
class and four macros in it that you can use as an implementation of an apply
method (stricly speaking, you could use any name for the method, but then the component usage won't look as natural). The macro you will use depends on if the component allows children and if the component accepts arbitrary DOM attributes (TagsMod
s).
auto
autoNoChildren
Cannot take DOM attrs autoNoTagMods
autoNoTagModsNoChildren
Each of the macros return type that signify what has been already processed (and thus cannot process it again).
auto
: WithProps
(properties have been consumed, thus can pass TagMod
s followed by children)autoNoChildren
: WithPropsNoChildren
(properties have been consumed, thus can pass TagMod
s, but that cannot be followed by children)autoNoTagMods
: WithPropsAndTags
(properties have been consumed as are TagMod
s, thus can be followed by children)autoNoTagModsNoChildren
: WithPropsAndTagsNoChildren
(properties, tags, and children have been consumed)ReactBridgeComponent
offers an easy way to bridge a component when an object extending it follows these conventions:
object MyComponent extends ReactBridgeComponent { ... }
, the correspoding MyComponent
is available in the global space.apply
methods taking properties as arguments. Each apply method may be implemented as either autoConstruct
or autoConstructNoChildren
. The default property transformation assumes that each method parameter type maps to the underlying component's expected property type and the parameter name match the underlying components property name. For example, if the underlying component expects a string property with name foo
, then the parameter type must be String
and parameter name must be foo
. The bridge automatically translates (through implicit converters) parameters with Seq
type (or its subtypes) to js array and Map
types with String
key type to js literal. You may provide custom conversions for your own types by introducing an implicit value of the JsWriter type.If a component cannot follow the expected conventions, it can override them as following:
componentName
supply a different name.componentNamespace
to supply the path to the function. For example, if the component function is exposed as foo.bar.MyComponent
, you can override componentNamespace
to return foo.bar
.componentValue
to use any js.Any
you can reference. This works well with @JSImport
ed objects.componentName
and/or componentNamespace
isn't sufficient, you may override jsComponent
to supply the component function.jsComponent
after transforming the method parameters appropriately.To import modules from NPM dependency, take a look at the following example:
object ReactBootstrapButton extends ReactBridgeComponent { @JSImport("react-bootstrap/Button", JSImport.Default) @js.native object RawComponent extends js.Object override lazy val componentValue = RawComponent def apply(variant: js.UndefOr[String] = js.undefined): WithProps = auto }Passing DOM attributes to component
Oftentimes, React components allow adding any DOM attributes in addition to properties specific to that component. By default, bridged components allow passing any DOM attributes (as TagMod
s). Behind the scene, these attributes are merged with specific propeties passed. If you don't want this behavior, you can use the appropriate varation of macro described in the table earlier.
Oftentimes, especially with components that simply enhance a regular DOM element such as <input> don't need any special properties beyond what can be passed as DOM attributes. To handle those cases, scalajs-react-bridge
offers ReactBridgeComponentNoSpecialProps
(which extends ReactBridgeComponent
). You can extend this class without implementing anything else thus making it a one-liner.
object Button extends ReactBridgeComponentNoSpecialProps
which then may be passed any DOM attributes (as TagMod):
Button(onClick --> handleClick)("Simple Button")Component without any special properties and without children
As a further special case, certain components may not take any children, either. Those components may extend ReactBridgeComponentNoSpecialPropsNoChildren
without implemented anything else thus making it a one-liner.
object Input extends ReactBridgeComponentNoPropsNoChildren
which then may be used as
Input(value := currentValue, onChange ==> handleChange)
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4