Current section

Files

Jump to
sprocket src sprocket@context.erl
Raw

src/sprocket@context.erl

-module(sprocket@context).
-compile([no_auto_import, nowarn_unused_vars]).
-export([dep/1, compare_deps/2, new/3, reset_for_render/1, fetch_or_init_hook/2, update_hook/3, push_event_handler/2, get_event_handler/2, dispatch_event/4]).
-export_type([element/0, event_handler/0, updater/1, dispatcher/0, hook_trigger/0, effect_result/0, callback_result/0, hook/0, compared/1, component_wip/0, context/0]).
-type element() :: {element,
binary(),
list(sprocket@html@attributes:attribute()),
list(element())} |
{component,
fun((context(), gleam@dynamic:dynamic_()) -> {context(),
list(element())}),
gleam@dynamic:dynamic_()} |
{debug, binary(), gleam@option:option(gleam@dynamic:dynamic_()), element()} |
{keyed, binary(), element()} |
{ignore_update, element()} |
{safe_html, binary()} |
{raw, binary()}.
-type event_handler() :: {event_handler,
sprocket@internal@utils@unique:unique(),
sprocket@internal@identifiable_callback:callback_fn()}.
-type updater(KVR) :: {updater, fun((KVR) -> {ok, nil} | {error, nil})}.
-type dispatcher() :: {dispatcher,
fun((binary(), binary(), gleam@option:option(binary())) -> {ok, nil} |
{error, nil})}.
-type hook_trigger() :: on_mount |
on_update |
{with_deps, list(gleam@dynamic:dynamic_())}.
-type effect_result() :: {effect_result,
gleam@option:option(fun(() -> nil)),
gleam@option:option(list(gleam@dynamic:dynamic_()))}.
-type callback_result() :: {callback_result,
sprocket@internal@identifiable_callback:callback_fn(),
gleam@option:option(list(gleam@dynamic:dynamic_()))}.
-type hook() :: {callback,
sprocket@internal@utils@unique:unique(),
sprocket@internal@identifiable_callback:callback_fn(),
hook_trigger(),
gleam@option:option(callback_result())} |
{effect,
sprocket@internal@utils@unique:unique(),
fun(() -> gleam@option:option(fun(() -> nil))),
hook_trigger(),
gleam@option:option(effect_result())} |
{reducer,
sprocket@internal@utils@unique:unique(),
gleam@dynamic:dynamic_(),
fun(() -> nil)} |
{state, sprocket@internal@utils@unique:unique(), gleam@dynamic:dynamic_()} |
{client,
sprocket@internal@utils@unique:unique(),
binary(),
gleam@option:option(fun((binary(), gleam@option:option(gleam@dynamic:dynamic_()), fun((binary(), gleam@option:option(binary())) -> {ok,
nil} |
{error, nil})) -> nil))}.
-type compared(KVS) :: {changed, KVS} | unchanged.
-type component_wip() :: {component_wip,
sprocket@internal@utils@ordered_map:ordered_map(integer(), hook()),
integer(),
boolean()}.
-type context() :: {context,
element(),
component_wip(),
list(event_handler()),
fun(() -> nil),
fun((sprocket@internal@utils@unique:unique(), fun((hook()) -> hook())) -> nil),
fun((sprocket@internal@utils@unique:unique(), binary(), gleam@option:option(binary())) -> {ok,
nil} |
{error, nil}),
gleam@erlang@process:subject(ids@cuid:message())}.
-spec dep(any()) -> gleam@dynamic:dynamic_().
dep(Dependency) ->
gleam@dynamic:from(Dependency).
-spec compare_deps(
list(gleam@dynamic:dynamic_()),
list(gleam@dynamic:dynamic_())
) -> compared(list(gleam@dynamic:dynamic_())).
compare_deps(Prev_deps, Deps) ->
case gleam@list:strict_zip(Prev_deps, Deps) of
{error, length_mismatch} ->
sprocket@internal@exceptions:throw_on_unexpected_deps_mismatch(
{<<"compare_deps"/utf8>>, Prev_deps, Deps}
);
{ok, Zipped_deps} ->
case gleam@list:all(
Zipped_deps,
fun(Z) ->
{A, B} = Z,
A =:= B
end
) of
true ->
unchanged;
_ ->
{changed, Deps}
end
end.
-spec new(
element(),
gleam@erlang@process:subject(ids@cuid:message()),
gleam@option:option(dispatcher())
) -> context().
new(View, Cuid_channel, Dispatcher) ->
{context,
View,
{component_wip, sprocket@internal@utils@ordered_map:new(), 0, true},
[],
fun() -> nil end,
fun(_, _) -> nil end,
fun(Id, Name, Payload) -> case Dispatcher of
{some, {dispatcher, Dispatch}} ->
Dispatch(
sprocket@internal@utils@unique:to_string(Id),
Name,
Payload
);
none ->
{error, nil}
end end,
Cuid_channel}.
-spec reset_for_render(context()) -> context().
reset_for_render(Ctx) ->
erlang:setelement(4, Ctx, []).
-spec fetch_or_init_hook(context(), fun(() -> hook())) -> {context(),
hook(),
integer()}.
fetch_or_init_hook(Ctx, Init) ->
Index = erlang:element(3, erlang:element(3, Ctx)),
Hooks = erlang:element(2, erlang:element(3, Ctx)),
case sprocket@internal@utils@ordered_map:get(Hooks, Index) of
{ok, Hook} ->
{erlang:setelement(
3,
Ctx,
erlang:setelement(3, erlang:element(3, Ctx), Index + 1)
),
Hook,
Index};
{error, nil} ->
case erlang:element(4, erlang:element(3, Ctx)) of
true ->
nil;
false ->
sprocket@internal@logger:error(
<<<<"
Hook not found for index: "/utf8,
(gleam@int:to_string(Index))/binary>>/binary,
". This indicates a hook was dynamically
created since first render which is not allowed.
"/utf8>>
),
erlang:error(#{gleam_error => panic,
message => <<"panic expression evaluated"/utf8>>,
module => <<"sprocket/context"/utf8>>,
function => <<"fetch_or_init_hook"/utf8>>,
line => 199})
end,
Hook@1 = Init(),
{erlang:setelement(
3,
Ctx,
{component_wip,
sprocket@internal@utils@ordered_map:insert(
erlang:element(2, erlang:element(3, Ctx)),
Index,
Hook@1
),
Index + 1,
erlang:element(4, erlang:element(3, Ctx))}
),
Hook@1,
Index}
end.
-spec update_hook(context(), hook(), integer()) -> context().
update_hook(Ctx, Hook, Index) ->
erlang:setelement(
3,
Ctx,
erlang:setelement(
2,
erlang:element(3, Ctx),
sprocket@internal@utils@ordered_map:update(
erlang:element(2, erlang:element(3, Ctx)),
Index,
Hook
)
)
).
-spec push_event_handler(
context(),
sprocket@internal@identifiable_callback:identifiable_callback()
) -> {context(), sprocket@internal@utils@unique:unique()}.
push_event_handler(Ctx, Identifiable_cb) ->
{identifiable_callback, Id, Cb} = Identifiable_cb,
{erlang:setelement(
4,
Ctx,
[{event_handler, Id, Cb} | erlang:element(4, Ctx)]
),
Id}.
-spec get_event_handler(context(), sprocket@internal@utils@unique:unique()) -> {context(),
{ok, event_handler()} | {error, nil}}.
get_event_handler(Ctx, Id) ->
Handler = gleam@list:find(
erlang:element(4, Ctx),
fun(H) ->
{event_handler, I, _} = H,
I =:= Id
end
),
{Ctx, Handler}.
-spec dispatch_event(
context(),
sprocket@internal@utils@unique:unique(),
binary(),
gleam@option:option(binary())
) -> {ok, nil} | {error, nil}.
dispatch_event(Ctx, Id, Name, Payload) ->
(erlang:element(7, Ctx))(Id, Name, Payload).