Skip to content

Instantly share code, notes, and snippets.

@ccapndave
Last active August 21, 2018 19:07
Show Gist options
  • Save ccapndave/4bd4aa5c70d0c1ff81cd7efbf597a819 to your computer and use it in GitHub Desktop.
Save ccapndave/4bd4aa5c70d0c1ff81cd7efbf597a819 to your computer and use it in GitHub Desktop.
module type GridInterface = {
type t('a);
let make: (~xNames: list(string), ~yNames: list(string)) => t('a);
let xNames: t('a) => list(string);
let yNames: t('a) => list(string);
let get: (t('a), ~x: int, ~y: int) => option('a);
/* let set: (t('a), ~x: int, ~y: int, ~value: 'a) => t('a); */
};
module Grid: GridInterface = {
type t('a) = {
xNames: list(string),
yNames: list(string),
data: array(array(option('a))),
};
let make = (~xNames, ~yNames) => {
xNames,
yNames,
data: Array.make_matrix(List.length(xNames), List.length(yNames), None),
};
let xNames = grid => grid.xNames;
let yNames = grid => grid.yNames;
let get = (grid, ~x, ~y) => grid.data->Array.get(x)->Array.get(y);
/* TODO
let set = (grid, ~x, ~y, ~value) => {
let newData = Array.copy(grid.data);
newData->Array.get(y)->Array.set(x, value);
{...grid, data: newData};
};*/
};
module Styles = {
open Css;
let grid =
style([
height(vh(100.)),
width(pct(100.0)),
borderCollapse(`collapse),
]);
let y = style([maxWidth(px(10))]);
let yText = style([transform(rotateZ(deg(-90))), textAlign(center)]);
let x = style([textAlign(center)]);
let gridCell =
style([
padding2(~v=px(5), ~h=px(20)),
border(px(1), solid, grey),
textAlign(center),
]);
};
let component = ReasonReact.statelessComponent("Grid");
let renderX = xNames =>
<tr>
<th />
{
xNames
|> List.mapi((idx, xName) =>
<th key={string_of_int(idx)} className=Styles.x>
{ReasonReact.string(xName)}
</th>
)
|> Array.of_list
|> ReasonReact.array
}
</tr>;
let make =
(
~grid: Grid.t('a),
~renderCell: 'a => ReasonReact.reactElement,
_children,
) => {
...component,
render: _self =>
<table className=Styles.grid>
<thead> {renderX(Grid.xNames(grid))} </thead>
<tbody>
{
Grid.yNames(grid)
|> List.mapi((y, yName) =>
<tr key={string_of_int(y)}>
<td className=Styles.y>
<div className=Styles.yText>
{ReasonReact.string(yName)}
</div>
</td>
{
Grid.xNames(grid)
|> List.mapi((x, _) => {
let cellContents = Grid.get(grid, ~x, ~y);
let renderedContent =
cellContents
->Belt.Option.mapWithDefault(
ReasonReact.null,
renderCell,
);
<td key={string_of_int(x)} className=Styles.gridCell>
renderedContent
</td>;
})
|> Array.of_list
|> ReasonReact.array
}
</tr>
)
|> Array.of_list
|> ReasonReact.array
}
</tbody>
</table>,
};
let yNames = ["Breakfast", "Lunch", "Dinner"];
let xNames = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
let grid = Grid.Grid.make(~xNames, ~yNames);
let renderCell = ReasonReact.string;
module SelectConfig = {
type t = string;
let id = data => data;
let toString = data => data;
};
module Select = Select.MakeSelect(SelectConfig);
ReactDOMRe.renderToElementWithId(
<div> <Select /> <Grid grid renderCell /> </div>,
"app",
);
module type SelectData = {
type t;
let id: t => string;
let toString: t => string;
};
module Styles = {
open Css;
let search = style([width(px(300)), fontSize(px(20))]);
};
module MakeSelect = (Data: SelectData) => {
type state = {
searchTerm: string,
results: Belt.List.t(Data.t),
};
type action =
| SetSearch(string);
let reactList = list => list->Belt.List.toArray->ReasonReact.array;
let component = ReasonReact.reducerComponent("Select");
let make = _children => {
...component,
initialState: () => {searchTerm: "", results: []},
reducer: (action: action, state: state) =>
switch (action) {
| SetSearch(searchTerm) => ReasonReact.Update({...state, searchTerm})
},
render: self => {
let renderedResults =
self.state.results
->Belt.List.map(result =>
<li key={Data.id(result)}>
{ReasonReact.string(result->Data.toString)}
</li>
);
<div>
<input
className=Styles.search
value={self.state.searchTerm}
onChange={
event =>
self.send(SetSearch(event->ReactEvent.Form.target##value))
}
/>
<ol> {reactList(renderedResults)} </ol>
</div>;
},
};
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment