Skip to content

Instantly share code, notes, and snippets.

@hew
Last active Apr 29, 2019
Embed
What would you like to do?
Reason React Native Styled System (Beta)

Reason React Native Styled System (Beta)

[@bs.module "react-native-responsive-fontsize"]
external rf: float => float = "default";
module type Config = {let scale: array(int);};
module TextMaker = (Config: Config) => {
open Belt.Option;
let systemize = shorthand =>
shorthand->mapWithDefault(0, m =>
switch (m) {
| 1
| 2
| 3
| 4
| 5 => Config.scale[m]
| _ => m
}
)
|> float_of_int;
let systemizeFont = v =>
v->mapWithDefault(16, m =>
switch (m) {
| 1
| 2
| 3
| 4
| 5 => Config.scale[m]
| _ => m
}
)
|> float_of_int;
let systemizeSize = v => v |> systemize |> Style.Size.pct;
let systemizeMargin = v => v |> systemize |> Style.Margin.pct;
[@react.component]
let make =
(
~py=?,
~px=?,
~p=?,
~mt=?,
~mb=?,
~ml=?,
~mr=?,
~size=?,
~style as styl: Style.t=?,
~color as clr=?,
~weight=?,
~align=?,
~children,
(),
) =>
<Text
style=Style.(
listOption([
mt->isSome
? Some(style(~marginTop=systemizeMargin(mt), ())) : None,
mb->isSome
? Some(style(~marginBottom=systemizeMargin(mb), ())) : None,
ml->isSome
? Some(style(~marginLeft=systemizeMargin(ml), ())) : None,
mr->isSome
? Some(style(~marginRight=systemizeMargin(mr), ())) : None,
p->isSome ? Some(style(~padding=systemizeSize(p), ())) : None,
py->isSome
? Some(style(~paddingTop=systemizeSize(py), ())) : None,
py->isSome
? Some(style(~paddingBottom=systemizeSize(py), ())) : None,
px->isSome
? Some(style(~paddingLeft=systemizeSize(px), ())) : None,
px->isSome
? Some(style(~paddingRight=systemizeSize(px), ())) : None,
size->isSome
? Some(style(~fontSize=systemizeFont(size), ())) : None,
clr->isSome
? Some(
style(
~color=clr->mapWithDefault("black", v => v) |> color,
(),
),
)
: None,
weight->isSome
? Some(
style(
~fontWeight=weight->mapWithDefault(`normal, w => w),
(),
),
)
: None,
align->isSome
? Some(
style(~textAlign=align->mapWithDefault(`center, w => w), ()),
)
: None,
Some(styl),
])
)>
children
</Text>;
};
module BoxMaker = (Config: Config) => {
open Belt.Option;
let systemize = shorthand =>
shorthand->mapWithDefault(0, m =>
switch (m) {
| 1
| 2
| 3
| 4
| 5 => Config.scale[m]
| _ => m
}
)
|> float_of_int;
let systemizeSize = v => v |> systemize |> rf |> Style.Size.pt;
let systemizeMargin = v => v |> systemize |> rf |> Style.Margin.pt;
[@react.component]
let make =
(
~py=?,
~px=?,
~p=?,
~mt=?,
~mb=?,
~ml=?,
~mr=?,
~bgColor=?,
~width=?,
~flex=?,
~content=?,
~direction=?,
~style as styl: Style.t=?,
~children=?,
(),
) =>
<View
style=Style.(
listOption([
mt->isSome
? Some(style(~marginTop=systemizeMargin(mt), ())) : None,
mb->isSome
? Some(style(~marginBottom=systemizeMargin(mb), ())) : None,
ml->isSome
? Some(style(~marginLeft=systemizeMargin(ml), ())) : None,
mr->isSome
? Some(style(~marginRight=systemizeMargin(mr), ())) : None,
p->isSome ? Some(style(~padding=systemizeSize(p), ())) : None,
py->isSome
? Some(style(~paddingTop=systemizeSize(py), ())) : None,
py->isSome
? Some(style(~paddingBottom=systemizeSize(py), ())) : None,
px->isSome
? Some(style(~paddingLeft=systemizeSize(px), ())) : None,
px->isSome
? Some(style(~paddingRight=systemizeSize(px), ())) : None,
width->isSome
? Some(
style(
~width=width->mapWithDefault(100., v => v) |> Size.pct,
(),
),
)
: None,
bgColor->isSome
? Some(
style(
~backgroundColor=
bgColor->mapWithDefault("black", v => v) |> color,
(),
),
)
: None,
content->isSome
? Some(
style(
~justifyContent=content->mapWithDefault(`flexStart, w => w),
(),
),
)
: None,
direction->isSome
? Some(
style(
~flexDirection=direction->mapWithDefault(`column, w => w),
(),
),
)
: None,
flex->isSome
? Some(style(~flex=flex->mapWithDefault(1., w => w), ())) : None,
Some(styl),
])
)>
{switch (children) {
| Some(child) => child
| None => React.null
}}
</View>;
};
module Box =
BoxMaker({
let scale = System.Scale.space;
});
module Txt =
TextMaker({
let scale = System.Scale.font;
});
@yawaramin
Copy link

yawaramin commented Apr 24, 2019

Some recommendations:

  • Avoid opening modules across such wide scopes, especially library modules like Belt.Option. IF you need a shorthand try aliasing it to a module, e.g. module BeltOpt = Belt.Option;
  • You're repeatedly defining the same function:
m =>
  switch (m) {
  | ...
  }

This can be extracted into a single, named, definition:

let scale = fun
  | ...
  | _ => m
  • Rather than mt->isSome ? Some(foo) : None, use mt->BeltOpt.map(_ => ... mt ...)
  • Rather than e.g. clr->mapWithDefault("black", v => v), use clr->BeltOpt.getWithDefault("black")
  • Instead of:
switch (children) {
       | Some(child) => child
       | None => React.null
}```

Use: `children->BeltOpt.getWithDefault(React.null)`

@hew
Copy link
Author

hew commented Apr 24, 2019

@yawaramin

Avoid opening modules across such wide scopes, especially library modules like Belt.Option. IF you need a shorthand try aliasing it to a module, e.g. module BeltOpt = Belt.Option;

Slightly off topic, but I sometimes see a flag in other people's bsconfig that is something to the effect of "-belt-open" and I always assumed it made Belt essentially globally available. Have you ever heard of that flag?

@yawaramin
Copy link

yawaramin commented Apr 24, 2019

@hew Yes, this flag is sometimes used to 'overlay' a new set of standard modules on top of the built-in ones that are available in OCaml. Typically it's desirable to open modules which will have as little effect on the global namespace as possible. So, open Belt is not too bad because it brings a small number of top-level modules into scope (as seen in https://bucklescript.github.io/bucklescript/api/Belt.html ). But open Belt.Option is a bit more frowned on because it will bring a large number of functions into scope. And at module scope it will pollute your namespace with lots of names. The more this is done, the harder it is to track where names are coming from and you have to keep referring to other parts of the code when you're reading some function, to figure it out.

@hew
Copy link
Author

hew commented Apr 24, 2019

I think I understand now. the global open only adds like 4 more things to the namespace, but Belt.Option has a bunch of functions, and opening it floods any given namespace with all of them. I'll default to something like let Opt = Belt.Option in the future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment