diff --git a/common/App.re b/common/App.re index cc3645978..0132379c9 100644 --- a/common/App.re +++ b/common/App.re @@ -18,6 +18,7 @@ let res = require('../plugins/res-syntax-highlightjs'); let bash = require('highlight.js/lib/languages/bash'); let json = require('highlight.js/lib/languages/json'); + let html = require('highlight.js/lib/languages/xml'); let ts = require('highlight.js/lib/languages/typescript'); let text = require('highlight.js/lib/languages/plaintext'); let diff = require('highlight.js/lib/languages/diff'); @@ -30,6 +31,7 @@ hljs.registerLanguage('sh', bash); hljs.registerLanguage('json', json); hljs.registerLanguage('text', text); + hljs.registerLanguage('html', html); hljs.registerLanguage('diff', diff); |}; @@ -110,6 +112,10 @@ let default = (props: props): React.element => { } | {base: [|"docs", "reason-compiler"|], version: Latest} => content + | {base: [|"docs", "react"|], version: Latest} => + frontmatter}> + content + | {base: [|"docs", "gentype"|], version: Latest} => content // common routes @@ -123,12 +129,17 @@ let default = (props: props): React.element => { // to keep the frontmatter parsing etc in one place content | _ => + let title = + switch (url) { + | {base: [|"docs"|]} => Some("Overview | ReScript Documentation") + | _ => None + }; - +
content
-
+ ; } }; }; diff --git a/common/HighlightJs.re b/common/HighlightJs.re index 37ae3b407..a5716cd8e 100644 --- a/common/HighlightJs.re +++ b/common/HighlightJs.re @@ -25,9 +25,12 @@ let renderHLJS = Js.String2.split(highlighted, "\n") ->Belt.Array.mapWithIndex((i, line) => if (Js.Array2.find(highlightedLines, lnum => lnum === i + 1) !== None) { - "" ++ line ++ ""; + let content = line === "" ? " " : line; + "" ++ content ++ ""; } else { - line; + "" + ++ line + ++ ""; } ) ->Js.Array2.joinWith("\n"); @@ -37,15 +40,8 @@ let renderHLJS = let dark = darkmode ? "dark" : ""; - ReactDOMRe.createElementVariadic( - "code", - ~props= - ReactDOMRe.objToDOMProps({ - "className": "hljs lang-" ++ lang ++ " " ++ dark, - "dangerouslySetInnerHTML": { - "__html": highlighted, - }, - }), - [||], - ); + ; }; diff --git a/components/VersionSelect.re b/components/VersionSelect.re index 1f148331d..f96334c78 100644 --- a/components/VersionSelect.re +++ b/components/VersionSelect.re @@ -17,7 +17,7 @@ let make = }, ); `) aren't syntactically valid; `type` is a reserved keyword in ReScript. Use `` instead. + +For `aria-*` use camelCasing, e.g., `ariaLabel`. For DOM components, we'll translate it to `aria-label` under the hood. + +For `data-*` this is a bit trickier; words with `-` in them aren't valid in ReScript. When you do want to write them, e.g., `
`, check out the [React.cloneElement](./rendering-elements#cloning-elements) or [React.createDOMElementVariadic](./rendering-elements#creating-dom-elements) section. + + +## Children Props + +In React `props.children` is a special attribute to represent the nested elements within a parent element: + +```res +let element =
child1 child2
+``` + +By default, whenever you are passing children like in the expression above, `children` will be treated +as a `React.element`: + + + +```res +module MyList = { + @react.component + let make = (~children: React.element) => { +
    + children +
+ } +} + + +
  • {React.string("Item 1")}
  • +
  • {React.string("Item 2")}
  • +
    +``` + +```js + +function MyList(Props) { + var children = Props.children; + return React.createElement("ul", undefined, children); +} + +var MyList = { + make: MyList +}; + +React.createElement(MyList, { + children: null + }, React.createElement("li", undefined, "Item 1"), + React.createElement("li", undefined, "Item 2")); +``` + +
    + +Interestingly, it doesn't matter if you are passing just one element, or several, React will always collapse its children to a single `React.element`. + +It is also possible to redefine the `children` type as well. Here are some examples: + +**Component with a mandatory `string` as children:** + +```res +module StringChildren = { + @react.component + let make = (~children: string) => { +
    + {React.string(children)} +
    + } +} + + "My Child" + +// This will cause a type check error + +``` + +**Component with an optional `React.element` as children:** + +```res +module OptionalChildren = { + @react.component + let make = (~children: option=?) => { +
    + {switch children { + | Some(element) => element + | None => React.string("No children provided") + }} +
    + } +} + +
    + +
    +
    +``` + +**Component that doesn't allow children at all:** + +```res +module NoChildren = { + @react.component + let make = () => { +
    + {React.string("I don't accept any children params")} +
    + } +} + +// The compiler will raise a type error here +
    +``` + +Children props are really tempting to be abused as a way to model hierarchies, e.g. ` ` (`List` should only allow `Item` / `ListHeader` elements), but this kind of constraint is hard to enforce because all components end up being a `React.element`, so it would require notorious runtime checking within `List` to verify that all children are in fact of type `Item` or `ListHeader`. + +The best way to approach this kind of issue is by using props instead of children, e.g. ``. This way it's easy to type check the constraints, and it also spares component consumers from memorizing and remembering component constraints. + +**The best use-case for `children` is to pass down `React.element`s without any semantic order or implementation details!** + + +## Props & Type Inference + +The ReScript type system is really good at inferring the prop types just by looking at its prop usage. + +For simple cases, well-scoped usage, or experimentation, it's still fine to omit type annotations: + + +```res +// Button.res + +@react.component +let make = (~onClick, ~msg, ~children) => { +
    + {React.string(msg)} + children +
    +} +``` + +In the example above, `onClick` will be inferred as `ReactEvent.Mouse.t => unit`, `msg` as `string` and `children` as `React.element`. Type inference is especially useful when you just forward values to some smaller (privately scoped) functions. + +Even tough type inference spares us a lot of keyboard typing, we still recommend to explicitly type your props (just like with any public API) for better type visibility and to prevent confusing type errors. + +## Using Components in JSX + +Every ReScript component can be used in JSX. For example, if we want to use our `Greeting` component within our `App` component, we can do this: + + + +```res +// src/App.re + +@react.component +let make = () => { +
    + +
    +} +``` +```js +var React = require("react"); +var Greeting = require("./Greeting.js") + +function App(Props) { + return React.createElement("div", undefined, React.createElement(Greeting.make, {})); +} + +var make = App; +``` + +
    + +**Note:** React components are capitalized; primitive DOM elements like `div` or `button` are uncapitalized. More infos on the JSX specifics and code transformations can be found in our [JSX language manual section](/docs/manual/latest/jsx#capitalized-tag). + + +### Handwritten Components + +You don't need to use the `@react.component` decorator to write components that can be used in JSX. Instead you can write a pair of `make` and `makeProps` functions such that type `makeProps: 'a => props` and `make: props => React.element` and these will always work as React components. + +This works with your own version of `[@bs.obj]`, or any other function that takes named args and returns a single props structure. For example: + + + +```res +module Link = { + type props = {"href": string, "children": React.element}; + @bs.obj external makeProps:( + ~href: string, + ~children: React.element, + unit) => props = "" + + let make = (props: props) => { + + {props["children"]} + + } +} + + {React.string("Docs")} +``` +```js +function Link(props) { + return React.createElement("a", { + href: props.href + }, props.children); +} + +React.createElement(Link, { + href: "/docs", + children: "Docs" + }); +``` + + + +More details on the `@react.component` decorator and its generated interface can be found in our [Beyond JSX](./beyond-jsx) page. + + +## Submodule Components + +We can also represent React components as submodules, which makes it very convenient to build more complex UI without the need to create multiple files for each composite component (that's probably only used by the parent component anyways): + +```res +// src/Button.res +module Label = { + @react.component + let make = (~title: string) => { +
    {React.string(title)}
    + } +} + +@react.component +let make = (~children) => { +
    +
    +} +``` + +The `Button.res` file defined above is now containing a `Label` component, that can also be used by other components, either by writing the fully qualified module name (``) or by using a module alias to shortcut the full qualifier: + + +```res +module Label = Button.Label + +let content =