Current section
Files
Jump to
Current section
Files
src/sketch@lustre.erl
-module(sketch@lustre).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-define(FILEPATH, "src/sketch/lustre.gleam").
-export([setup/0, construct/1, teardown/1, render/3, node/0, ssr/1]).
-export_type([container/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-opaque container() :: {document, gleam@dynamic:dynamic_()} |
{shadow, gleam@dynamic:dynamic_()} |
node.
-file("src/sketch/lustre.gleam", 38).
?DOC(
" Setup the StyleSheet to use across your application. At least one stylesheet\n"
" must be set in your entire application. `setup` should be called once for\n"
" every stylesheet you'll use.\n"
"\n"
" In `sketch_lustre`, there's no notion of persistent or ephemeral stylesheets:\n"
" all stylesheets will be persisted in the application.\n"
"\n"
" In case you need to add initial styling directly in your stylesheet, take a\n"
" look at [`construct`](#construct).\n"
"\n"
" ```gleam\n"
" pub fn main() {\n"
" let assert Ok(stylesheet) = sketch_lustre.setup()\n"
" // The following code goes there.\n"
" }\n"
" ```\n"
).
-spec setup() -> {ok, sketch:style_sheet()} | {error, nil}.
setup() ->
case sketch:stylesheet(persistent) of
{error, _} ->
{error, nil};
{ok, Stylesheet} ->
sketch_global_ffi:set_stylesheet(Stylesheet)
end.
-file("src/sketch/lustre.gleam", 64).
?DOC(
" Setup the StyleSheet to use across your application, and let you inject\n"
" initial styling directly in your stylesheet. If you don't need to add\n"
" default styling, you can take a look at [`setup`](#setup).\n"
"\n"
" In `sketch_lustre`, there's no notion of persistent or ephemeral stylesheets:\n"
" all stylesheets will be persisted in the application.\n"
"\n"
" ```gleam\n"
" pub fn main() {\n"
" let assert Ok(stylesheet) =\n"
" sketch_lustre.construct(fn (stylesheet) {\n"
" stylesheet\n"
" |> sketch.global(css.global(\"html\", [...]))\n"
" |> sketch.global(css.global(\"body\", [...]))\n"
" |> sketch.global(css.global(\":root\", [...]))\n"
" })\n"
" // The following code goes there.\n"
" }\n"
" ```\n"
).
-spec construct(fun((sketch:style_sheet()) -> sketch:style_sheet())) -> {ok,
sketch:style_sheet()} |
{error, nil}.
construct(Init) ->
case sketch:stylesheet(persistent) of
{error, _} ->
{error, nil};
{ok, Stylesheet} ->
sketch_global_ffi:set_stylesheet(Init(Stylesheet))
end.
-file("src/sketch/lustre.gleam", 84).
?DOC(
" Unref the StyleSheet to let it be garbaged by the runtime. Because stylesheets\n"
" are memoized to guarantee performance and usability of Sketch Lustre, they\n"
" should be dereferenced to ensure no memory leaks will happen in the application.\n"
"\n"
" ```gleam\n"
" pub fn main() {\n"
" let assert Ok(stylesheet) = sketch_lustre.setup()\n"
" ...\n"
" let assert Ok(_) = sketch_lustre.teardown(stylesheet)\n"
" }\n"
" ```\n"
).
-spec teardown(sketch:style_sheet()) -> {ok, nil} | {error, nil}.
teardown(Stylesheet) ->
gleam@result:map(
sketch_global_ffi:teardown_stylesheet(Stylesheet),
fun(_) -> sketch:dispose(Stylesheet) end
).
-file("src/sketch/lustre.gleam", 112).
?DOC(
" Use `render` as a view middleware. Like in Wisp, `sketch_lustre`\n"
" adopts the middleware philosophy in Lustre, and allows you to call `render`\n"
" directly in your view function, by using `use`. No need to wrap your view\n"
" function.\n"
"\n"
" ```gleam\n"
" import lustre\n"
" import lustre/element\n"
" import sketch\n"
" import sketch/lustre as sketch_lustre\n"
" import sketch/lustre/element/html\n"
"\n"
" pub fn main() {\n"
" let assert Ok(stylesheet) = sketch_lustre.setup()\n"
" lustre.simple(init, update, view(_, stylesheet))\n"
" |> lustre.start(\"#root\", Nil)\n"
" }\n"
"\n"
" fn view(model: model, stylesheet: sketch.StyleSheet) -> element.Element(msg) {\n"
" use <- skech_lustre.render(stylesheet, in: [sketch_lustre.node()])\n"
" html.div([], [])\n"
" }\n"
" ```\n"
).
-spec render(
sketch:style_sheet(),
list(container()),
fun(() -> lustre@vdom@vnode:element(OYD))
) -> lustre@vdom@vnode:element(OYD).
render(Stylesheet, Outputs, View) ->
case sketch_global_ffi:set_current_stylesheet(Stylesheet) of
{ok, _} -> nil;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"sketch/lustre"/utf8>>,
function => <<"render"/utf8>>,
line => 117,
value => _assert_fail,
start => 3944,
'end' => 4004,
pattern_start => 3955,
pattern_end => 3960})
end,
New_view = View(),
case sketch_global_ffi:get_stylesheet() of
{error, _} ->
New_view;
{ok, Stylesheet@1} ->
Content = sketch:render(Stylesheet@1),
gleam@list:fold(
Outputs,
New_view,
fun(View@1, Stylesheet@2) -> case Stylesheet@2 of
node ->
lustre@element:fragment(
[lustre@element@html:style([], Content), View@1]
);
{document, Css_stylesheet} ->
gleam@function:tap(
View@1,
fun(_) ->
_ = sketch_global_ffi:dismiss_current_stylesheet(
),
sketch@lustre@internals@css_stylesheet:replace(
Content,
Css_stylesheet
)
end
);
{shadow, Css_stylesheet} ->
gleam@function:tap(
View@1,
fun(_) ->
_ = sketch_global_ffi:dismiss_current_stylesheet(
),
sketch@lustre@internals@css_stylesheet:replace(
Content,
Css_stylesheet
)
end
)
end end
)
end.
-file("src/sketch/lustre.gleam", 211).
?DOC(
" Create a container in the window document. Create a `CSSStyleSheet` and\n"
" updates the content directly inside. \\\n"
" Because `document` is only accessible in the browser, that function cannot\n"
" be called on Erlang target.\n"
" Create a container in a Shadow Root. Create a `CSSStyleSheet` and\n"
" updates the content directly inside. \\\n"
" Because `document` is only accessible in the browser, that function cannot\n"
" be called on Erlang target.\n"
" Create a container directly in the DOM. CSS will be put directly in the\n"
" elements tree, in a `<style>` tag.\n"
).
-spec node() -> container().
node() ->
node.
-file("src/sketch/lustre.gleam", 215).
-spec contains_head(lustre@vdom@vnode:element(any())) -> boolean().
contains_head(El) ->
case El of
{element, _, _, _, _, <<"head"/utf8>>, _, _, _, _, _} ->
true;
{element, _, _, _, _, _, _, _, _, _, _} ->
gleam@list:fold(
erlang:element(8, El),
false,
fun(Acc, Val) -> Acc orelse contains_head(Val) end
);
_ ->
false
end.
-file("src/sketch/lustre.gleam", 226).
-spec put_in_head(lustre@vdom@vnode:element(OYL), binary()) -> lustre@vdom@vnode:element(OYL).
put_in_head(El, Content) ->
case El of
{element, _, _, _, _, <<"head"/utf8>>, _, _, _, _, _} ->
_record = El,
{element,
erlang:element(2, _record),
erlang:element(3, _record),
erlang:element(4, _record),
erlang:element(5, _record),
erlang:element(6, _record),
erlang:element(7, _record),
begin
Style = lustre@element@html:style([], Content),
[Style | erlang:element(8, El)]
end,
erlang:element(9, _record),
erlang:element(10, _record),
erlang:element(11, _record)};
{element, _, _, _, _, <<"html"/utf8>>, _, _, _, _, _} ->
_record@1 = El,
{element,
erlang:element(2, _record@1),
erlang:element(3, _record@1),
erlang:element(4, _record@1),
erlang:element(5, _record@1),
erlang:element(6, _record@1),
erlang:element(7, _record@1),
begin
gleam@list:map(
erlang:element(8, El),
fun(Child) -> put_in_head(Child, Content) end
)
end,
erlang:element(9, _record@1),
erlang:element(10, _record@1),
erlang:element(11, _record@1)};
Node ->
Node
end.
-file("src/sketch/lustre.gleam", 175).
?DOC(
" Use `ssr` as a view middleware. Like in Wisp, `sketch_lustre`\n"
" adopts the middleware philosophy in Lustre, and allows you to call `ssr`\n"
" directly in your view function, by using `use`. No need to wrap your view\n"
" function.\n"
"\n"
" ```gleam\n"
" import gleam/bytes_tree\n"
" import gleam/http/response\n"
" import lustre\n"
" import lustre/element\n"
" import mist\n"
" import sketch\n"
" import sketch/lustre as sketch_lustre\n"
" import sketch/lustre/element/html\n"
" import wisp\n"
"\n"
" pub fn main() {\n"
" let assert Ok(stylesheet) = sketch_lustre.setup()\n"
" let assert Ok(_) =\n"
" fn(_) { greet(stylesheet) }\n"
" |> mist.new()\n"
" |> mist.port(1234)\n"
" |> mist.start_http()\n"
" process.sleep_forever()\n"
" }\n"
"\n"
" pub fn greet(stylesheet: sketch.StyleSheet) -> wisp.Response {\n"
" view(model, stylesheet)\n"
" |> element.to_document_string\n"
" |> bytes_tree.to_document_string\n"
" |> mist.Bytes\n"
" |> response.set_body(response.new(200), _)\n"
" }\n"
"\n"
" fn view(model: model, stylesheet: sketch.StyleSheet) -> element.Element(msg) {\n"
" use <- skech_lustre.ssr(stylesheet)\n"
" html.div([], [])\n"
" }\n"
" ```\n"
).
-spec ssr(fun(() -> lustre@vdom@vnode:element(OYG))) -> lustre@vdom@vnode:element(OYG).
ssr(View) ->
New_view = View(),
case sketch_global_ffi:get_stylesheet() of
{error, _} ->
New_view;
{ok, Stylesheet} ->
Content = sketch:render(Stylesheet),
case contains_head(New_view) of
true ->
put_in_head(New_view, Content);
false ->
lustre@element:fragment(
[lustre@element@html:style([], Content), New_view]
)
end
end.