-module(dnd@groups). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dnd/groups.gleam"). -export([is_touch_selecting/1, get_selected_index/1, touch_selected_styles/2, create/2]). -export_type([position/0, movement/0, input_mode/0, listen/0, operation/0, groups_config/1, config/1, state/0, touch_state/0, touch_pending_state/0, interaction_state/0, model/0, info/0, system/2, dnd_msg/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. -type position() :: {position, float(), float()}. -type movement() :: free | horizontal | vertical. -type input_mode() :: mouse_only | touch_only | auto. -type listen() :: on_drag | on_drop. -type operation() :: insert_after | insert_before | rotate | swap | unaltered. -type groups_config(NLL) :: {groups_config, listen(), operation(), fun((NLL, NLL) -> boolean()), fun((NLL, NLL) -> NLL)}. -type config(NLM) :: {config, fun((integer(), integer(), list(NLM)) -> list(NLM)), movement(), listen(), operation(), groups_config(NLM), input_mode(), integer(), integer(), integer(), integer()}. -type state() :: {state, integer(), integer(), integer(), position(), position(), binary(), binary()}. -type touch_state() :: {touch_state, integer(), binary(), gleam@option:option(position()), boolean(), gleam@option:option(position())}. -type touch_pending_state() :: {touch_pending_state, integer(), binary(), position(), boolean()}. -type interaction_state() :: idle | {mouse_dragging, state()} | {touch_pending, touch_pending_state()} | {touch_selecting, touch_state()}. -type model() :: {model, interaction_state(), integer(), boolean()}. -type info() :: {info, integer(), integer(), binary(), binary(), position(), position()}. -type system(NLN, NLO) :: {system, model(), fun((dnd_msg(), model(), list(NLN)) -> {model(), list(NLN)}), fun((integer(), binary()) -> list(lustre@vdom@vattr:attribute(NLO))), fun((integer(), binary()) -> list(lustre@vdom@vattr:attribute(NLO))), fun((model()) -> list(lustre@vdom@vattr:attribute(NLO))), fun((model()) -> gleam@option:option(info())), fun((dnd_msg(), model(), list(NLN)) -> {model(), list(NLN), lustre@effect:effect(NLO)}), fun((integer(), binary()) -> list(lustre@vdom@vattr:attribute(NLO))), fun((integer()) -> list(lustre@vdom@vattr:attribute(NLO))), fun(() -> list(lustre@vdom@vattr:attribute(NLO))), fun((model()) -> boolean()), fun((model()) -> gleam@option:option(integer())), fun((model(), integer()) -> list(lustre@vdom@vattr:attribute(NLO)))}. -type dnd_msg() :: {drag_start, integer(), binary(), position()} | {drag, position()} | {drag_over, integer(), binary()} | {drag_enter, integer()} | drag_leave | drag_end | {touch_start, integer(), binary(), position()} | {touch_move, position()} | {touch_end, position()} | {touch_select, integer(), binary()} | {touch_drop_start, position()} | {touch_drop, integer(), position()} | touch_cancel | {touch_timeout, integer()} | {touch_duration_met, integer()} | {touch_drop_cooldown_complete, integer()}. -file("src/dnd/groups.gleam", 1053). -spec schedule_hold_duration(integer(), integer(), fun((dnd_msg()) -> NME)) -> lustre@effect:effect(NME). schedule_hold_duration(Duration_ms, Generation, Step_msg) -> lustre@effect:from( fun(Dispatch) -> timer_ffi:set_timeout( Duration_ms, fun() -> Dispatch(Step_msg({touch_duration_met, Generation})) end ) end ). -file("src/dnd/groups.gleam", 1065). -spec schedule_drop_cooldown(integer(), integer(), fun((dnd_msg()) -> NMG)) -> lustre@effect:effect(NMG). schedule_drop_cooldown(Cooldown_ms, Generation, Step_msg) -> lustre@effect:from( fun(Dispatch) -> timer_ffi:set_timeout( Cooldown_ms, fun() -> Dispatch( Step_msg({touch_drop_cooldown_complete, Generation}) ) end ) end ). -file("src/dnd/groups.gleam", 1077). -spec schedule_timeout(integer(), integer(), fun((dnd_msg()) -> NMI)) -> lustre@effect:effect(NMI). schedule_timeout(Timeout_ms, Generation, Step_msg) -> lustre@effect:from( fun(Dispatch) -> timer_ffi:set_timeout( Timeout_ms, fun() -> Dispatch(Step_msg({touch_timeout, Generation})) end ) end ). -file("src/dnd/groups.gleam", 1106). -spec get_items_at_index(integer(), list(NMM)) -> list(NMM). get_items_at_index(Index, List) -> _pipe = List, _pipe@1 = gleam@list:drop(_pipe, Index), gleam@list:take(_pipe@1, 1). -file("src/dnd/groups.gleam", 1091). -spec groups_equal( fun((NMK, NMK) -> boolean()), integer(), integer(), list(NMK) ) -> boolean(). groups_equal(Comparator, Drag_index, Drop_index, List) -> Drag_items = get_items_at_index(Drag_index, List), Drop_items = get_items_at_index(Drop_index, List), case {Drag_items, Drop_items} of {[Drag_item], [Drop_item]} -> Comparator(Drag_item, Drop_item); {_, _} -> false end. -file("src/dnd/groups.gleam", 1110). -spec drag_group_update(fun((NMP, NMP) -> NMP), integer(), integer(), list(NMP)) -> list(NMP). drag_group_update(Setter, Drag_index, Drop_index, List) -> Drop_items = get_items_at_index(Drop_index, List), _pipe = gleam@list:index_map( List, fun(Item, Index) -> case Index =:= Drag_index of true -> case Drop_items of [Drop_item] -> [Setter(Drop_item, Item)]; _ -> [Item] end; false -> [Item] end end ), lists:append(_pipe). -file("src/dnd/groups.gleam", 1132). -spec drag_and_drop_group_update( fun((NMS, NMS) -> NMS), integer(), integer(), list(NMS) ) -> list(NMS). drag_and_drop_group_update(Setter, Drag_index, Drop_index, List) -> Drag_items = get_items_at_index(Drag_index, List), Drop_items = get_items_at_index(Drop_index, List), _pipe = gleam@list:index_map(List, fun(Item, Index) -> case Index of I when I =:= Drag_index -> case Drop_items of [Drop_item] -> [Setter(Drop_item, Item)]; _ -> [Item] end; I@1 when I@1 =:= Drop_index -> case Drag_items of [Drag_item] -> [Setter(Drag_item, Item)]; _ -> [Item] end; _ -> [Item] end end), lists:append(_pipe). -file("src/dnd/groups.gleam", 1161). -spec all_group_update( fun((list(NMV)) -> list(NMV)), integer(), integer(), list(NMV) ) -> list(NMV). all_group_update(Fn_transform, Start_index, End_index, List) -> Beginning = gleam@list:take(List, Start_index), Middle = begin _pipe = List, _pipe@1 = gleam@list:drop(_pipe, Start_index), gleam@list:take(_pipe@1, (End_index - Start_index) + 1) end, End = gleam@list:drop(List, End_index + 1), lists:append(lists:append(Beginning, Fn_transform(Middle)), End). -file("src/dnd/groups.gleam", 1216). -spec sublist_by_first_item(fun((NND, NND) -> boolean()), list(NND)) -> list(NND). sublist_by_first_item(Comparator, Items) -> case Items of [] -> []; [First | _] -> gleam@list:filter(Items, fun(Item) -> Comparator(First, Item) end) end. -file("src/dnd/groups.gleam", 1175). -spec bubble_group_recursive( fun((NNA, NNA) -> boolean()), fun((NNA, NNA) -> NNA), list(NNA) ) -> list(NNA). bubble_group_recursive(Comparator, Setter, Items) -> case Items of [] -> []; [X] -> [X]; [X@1 | Xs] -> Sublist = sublist_by_first_item(Comparator, Items), case Sublist of [] -> [X@1 | Xs]; _ -> Sublist_length = erlang:length(Sublist), Reversed_tail = begin _pipe = Sublist, _pipe@1 = gleam@list:drop(_pipe, 1), lists:reverse(_pipe@1) end, First_sublist = gleam@list:take(Sublist, 1), Next_item = begin _pipe@2 = Xs, _pipe@3 = gleam@list:drop(_pipe@2, Sublist_length - 1), gleam@list:take(_pipe@3, 1) end, case {First_sublist, Next_item} of {[First], [Next]} -> Updated_first = Setter(Next, First), Updated_next = Setter(First, Next), Remaining = gleam@list:drop(Xs, Sublist_length), lists:append( lists:append(Reversed_tail, [Updated_first]), bubble_group_recursive( Comparator, Setter, lists:append([Updated_next], Remaining) ) ); {_, _} -> [X@1 | Xs] end end end. -file("src/dnd/groups.gleam", 1228). -spec update_state_for_operation(operation(), integer(), state()) -> state(). update_state_for_operation(Operation, Drop_index, State) -> case Operation of insert_after -> New_drag_index = case Drop_index < erlang:element(2, State) of true -> Drop_index + 1; false -> Drop_index end, {state, New_drag_index, erlang:element(3, State), 0, erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)}; insert_before -> New_drag_index@1 = case erlang:element(2, State) < Drop_index of true -> Drop_index - 1; false -> Drop_index end, {state, New_drag_index@1, erlang:element(3, State), 0, erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)}; rotate -> {state, Drop_index, erlang:element(3, State), 0, erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)}; swap -> {state, Drop_index, erlang:element(3, State), 0, erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)}; unaltered -> {state, erlang:element(2, State), erlang:element(3, State), 0, erlang:element(5, State), erlang:element(6, State), erlang:element(7, State), erlang:element(8, State)} end. -file("src/dnd/groups.gleam", 1255). -spec sublist_update(operation(), integer(), integer(), list(NNG)) -> list(NNG). sublist_update(Operation, Drag_index, Drop_index, List) -> case Operation of insert_after -> dnd:insert_after(Drag_index, Drop_index, List); insert_before -> dnd:insert_before(Drag_index, Drop_index, List); rotate -> dnd:rotate_items(Drag_index, Drop_index, List); swap -> dnd:swap_items(Drag_index, Drop_index, List); unaltered -> List end. -file("src/dnd/groups.gleam", 1271). -spec groups_list_update( operation(), fun((NNJ, NNJ) -> boolean()), fun((NNJ, NNJ) -> NNJ), integer(), integer(), list(NNJ) ) -> list(NNJ). groups_list_update(Operation, Comparator, Setter, Drag_index, Drop_index, List) -> case Operation of insert_after -> Updated_list = drag_group_update( Setter, Drag_index, Drop_index, List ), dnd:insert_after(Drag_index, Drop_index, Updated_list); insert_before -> Updated_list@1 = drag_group_update( Setter, Drag_index, Drop_index, List ), dnd:insert_before(Drag_index, Drop_index, Updated_list@1); rotate -> case Drag_index < Drop_index of true -> Transform_fn = fun(Items) -> Reversed = lists:reverse(Items), Bubbled = bubble_group_recursive( Comparator, Setter, Reversed ), lists:reverse(Bubbled) end, Updated_list@2 = all_group_update( Transform_fn, Drag_index, Drop_index, List ), dnd:rotate_items(Drag_index, Drop_index, Updated_list@2); false -> Transform_fn@1 = fun(Items@1) -> bubble_group_recursive(Comparator, Setter, Items@1) end, Updated_list@3 = all_group_update( Transform_fn@1, Drop_index, Drag_index, List ), dnd:rotate_items(Drag_index, Drop_index, Updated_list@3) end; swap -> Updated_list@4 = drag_and_drop_group_update( Setter, Drag_index, Drop_index, List ), dnd:swap_items(Drag_index, Drop_index, Updated_list@4); unaltered -> List end. -file("src/dnd/groups.gleam", 1321). -spec drag_events(fun((dnd_msg()) -> NNM)) -> fun((integer(), binary()) -> list(lustre@vdom@vattr:attribute(NNM))). drag_events(Step_msg) -> fun(Drag_index, Drag_element_id) -> [lustre@event:on( <<"mousedown"/utf8>>, begin gleam@dynamic@decode:field( <<"clientX"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_float/1}, fun(Client_x) -> gleam@dynamic@decode:field( <<"clientY"/utf8>>, {decoder, fun gleam@dynamic@decode:decode_float/1}, fun(Client_y) -> gleam@dynamic@decode:success( Step_msg( {drag_start, Drag_index, Drag_element_id, {position, Client_x, Client_y}} ) ) end ) end ) end )] end. -file("src/dnd/groups.gleam", 1341). -spec drop_events(fun((dnd_msg()) -> NNP)) -> fun((integer(), binary()) -> list(lustre@vdom@vattr:attribute(NNP))). drop_events(Step_msg) -> fun(Drop_index, Drop_element_id) -> [lustre@event:on( <<"mouseover"/utf8>>, gleam@dynamic@decode:success( Step_msg({drag_over, Drop_index, Drop_element_id}) ) ), lustre@event:on( <<"mouseenter"/utf8>>, gleam@dynamic@decode:success(Step_msg({drag_enter, Drop_index})) ), lustre@event:on( <<"mouseleave"/utf8>>, gleam@dynamic@decode:success(Step_msg(drag_leave)) )] end. -file("src/dnd/groups.gleam", 1360). ?DOC( " Decoder for touch position from touch events.\n" " Extracts clientX/clientY from the first touch in changedTouches.\n" ). -spec decode_touch_position() -> gleam@dynamic@decode:decoder(position()). decode_touch_position() -> gleam@dynamic@decode:subfield( [<<"changedTouches"/utf8>>, <<"0"/utf8>>, <<"clientX"/utf8>>], {decoder, fun gleam@dynamic@decode:decode_float/1}, fun(X) -> gleam@dynamic@decode:subfield( [<<"changedTouches"/utf8>>, <<"0"/utf8>>, <<"clientY"/utf8>>], {decoder, fun gleam@dynamic@decode:decode_float/1}, fun(Y) -> gleam@dynamic@decode:success({position, X, Y}) end ) end ). -file("src/dnd/groups.gleam", 1369). ?DOC( " Touch grip events - touchstart captures position, touchmove tracks movement,\n" " touchend validates tap vs scroll. Apply to the grip handle or the entire draggable item.\n" " Uses preventDefault on touchstart to stop browser from synthesizing mouse events.\n" ). -spec touch_grip_events(fun((dnd_msg()) -> NNT)) -> fun((integer(), binary()) -> list(lustre@vdom@vattr:attribute(NNT))). touch_grip_events(Step_msg) -> fun(Index, Element_id) -> [lustre@event:on( <<"touchstart"/utf8>>, begin gleam@dynamic@decode:then( decode_touch_position(), fun(Position) -> gleam@dynamic@decode:success( Step_msg( {touch_start, Index, Element_id, Position} ) ) end ) end ), lustre@event:on( <<"touchmove"/utf8>>, begin gleam@dynamic@decode:then( decode_touch_position(), fun(Position@1) -> gleam@dynamic@decode:success( Step_msg({touch_move, Position@1}) ) end ) end ), lustre@event:on( <<"touchend"/utf8>>, begin gleam@dynamic@decode:then( decode_touch_position(), fun(Position@2) -> gleam@dynamic@decode:success( Step_msg({touch_end, Position@2}) ) end ) end ), lustre@event:on( <<"touchcancel"/utf8>>, gleam@dynamic@decode:success(Step_msg(touch_cancel)) )] end. -file("src/dnd/groups.gleam", 1397). -spec touch_drop_zone_events(fun((dnd_msg()) -> NNW)) -> fun((integer()) -> list(lustre@vdom@vattr:attribute(NNW))). touch_drop_zone_events(Step_msg) -> fun(Drop_index) -> [lustre@event:on( <<"touchstart"/utf8>>, begin gleam@dynamic@decode:then( decode_touch_position(), fun(Position) -> gleam@dynamic@decode:success( Step_msg({touch_drop_start, Position}) ) end ) end ), lustre@event:on( <<"touchend"/utf8>>, begin gleam@dynamic@decode:then( decode_touch_position(), fun(Position@1) -> gleam@dynamic@decode:success( Step_msg({touch_drop, Drop_index, Position@1}) ) end ) end )] end. -file("src/dnd/groups.gleam", 1420). ?DOC( " Cancel events - currently returns empty list.\n" " Browser-initiated cancellation is handled by the `touchcancel` listener in\n" " `touch_grip_events`; `touch_timeout_ms` remains a backstop. This hook is for\n" " an optional explicit cancel affordance (e.g. a button) that sends TouchCancel.\n" ). -spec cancel_events(fun((dnd_msg()) -> NNZ)) -> fun(() -> list(lustre@vdom@vattr:attribute(NNZ))). cancel_events(_) -> fun() -> [] end. -file("src/dnd/groups.gleam", 1426). -spec ghost_styles(movement()) -> fun((model()) -> list(lustre@vdom@vattr:attribute(any()))). ghost_styles(Movement) -> fun(Model) -> case erlang:element(2, Model) of {mouse_dragging, State} -> Essential_styles = [lustre@attribute:style( <<"position"/utf8>>, <<"fixed"/utf8>> ), lustre@attribute:style( <<"pointer-events"/utf8>>, <<"none"/utf8>> ), lustre@attribute:style(<<"z-index"/utf8>>, <<"1000"/utf8>>), lustre@attribute:style( <<"user-select"/utf8>>, <<"none"/utf8>> ), lustre@attribute:style( <<"-webkit-user-select"/utf8>>, <<"none"/utf8>> )], Position_styles = case Movement of free -> [lustre@attribute:style( <<"left"/utf8>>, <<(gleam_stdlib:float_to_string( erlang:element( 2, erlang:element(6, State) ) ))/binary, "px"/utf8>> ), lustre@attribute:style( <<"top"/utf8>>, <<(gleam_stdlib:float_to_string( erlang:element( 3, erlang:element(6, State) ) ))/binary, "px"/utf8>> )]; horizontal -> [lustre@attribute:style( <<"left"/utf8>>, <<(gleam_stdlib:float_to_string( erlang:element( 2, erlang:element(6, State) ) ))/binary, "px"/utf8>> ), lustre@attribute:style( <<"top"/utf8>>, <<(gleam_stdlib:float_to_string( erlang:element( 3, erlang:element(5, State) ) ))/binary, "px"/utf8>> )]; vertical -> [lustre@attribute:style( <<"left"/utf8>>, <<(gleam_stdlib:float_to_string( erlang:element( 2, erlang:element(5, State) ) ))/binary, "px"/utf8>> ), lustre@attribute:style( <<"top"/utf8>>, <<(gleam_stdlib:float_to_string( erlang:element( 3, erlang:element(6, State) ) ))/binary, "px"/utf8>> )] end, lists:append(Essential_styles, Position_styles); {touch_pending, _} -> []; {touch_selecting, _} -> []; idle -> [] end end. -file("src/dnd/groups.gleam", 1483). -spec get_info(model()) -> gleam@option:option(info()). get_info(Model) -> case erlang:element(2, Model) of {mouse_dragging, State} -> {some, {info, erlang:element(2, State), erlang:element(3, State), erlang:element(7, State), erlang:element(8, State), erlang:element(5, State), erlang:element(6, State)}}; {touch_pending, _} -> none; {touch_selecting, _} -> none; idle -> none end. -file("src/dnd/groups.gleam", 1503). -spec is_touch_selecting(model()) -> boolean(). is_touch_selecting(Model) -> case erlang:element(2, Model) of {touch_selecting, _} -> true; _ -> false end. -file("src/dnd/groups.gleam", 1510). -spec get_selected_index(model()) -> gleam@option:option(integer()). get_selected_index(Model) -> case erlang:element(2, Model) of {touch_selecting, Touch_state} -> {some, erlang:element(2, Touch_state)}; _ -> none end. -file("src/dnd/groups.gleam", 1527). ?DOC( " Returns pop animation styles for the selected item in touch mode.\n" " Apply these styles to the item element at the given index.\n" " Returns empty list if the index doesn't match the selected item.\n" "\n" " The animation uses CSS keyframes which must be defined in your stylesheet:\n" " ```css\n" " @keyframes dnd-pop {\n" " 50% { transform: scale(1.2); }\n" " }\n" " ```\n" ). -spec touch_selected_styles(model(), integer()) -> list(lustre@vdom@vattr:attribute(any())). touch_selected_styles(Model, Index) -> case erlang:element(2, Model) of {touch_selecting, Touch_state} when erlang:element(2, Touch_state) =:= Index -> [lustre@attribute:style( <<"animation"/utf8>>, <<"dnd-pop 0.2s ease-out"/utf8>> )]; _ -> [] end. -file("src/dnd/groups.gleam", 1539). ?DOC(" Calculate the distance between two positions.\n"). -spec distance(position(), position()) -> float(). distance(P1, P2) -> Dx = erlang:element(2, P2) - erlang:element(2, P1), Dy = erlang:element(3, P2) - erlang:element(3, P1), _pipe = gleam@float:square_root((Dx * Dx) + (Dy * Dy)), gleam@result:unwrap(_pipe, +0.0). -file("src/dnd/groups.gleam", 1547). ?DOC(" Check if the distance between two positions is within a threshold.\n"). -spec is_within_threshold(position(), position(), integer()) -> boolean(). is_within_threshold(P1, P2, Threshold) -> distance(P1, P2) < erlang:float(Threshold). -file("src/dnd/groups.gleam", 420). -spec system_update(config(NLU)) -> fun((dnd_msg(), model(), list(NLU)) -> {model(), list(NLU)}). system_update(Config) -> fun(Msg, Model, List) -> case {Msg, erlang:element(2, Model)} of {{drag_start, _, _, _}, idle} when erlang:element(4, Model) -> {{model, erlang:element(2, Model), erlang:element(3, Model), false}, List}; {{drag_start, Drag_index, Drag_element_id, Position}, idle} -> New_state = {state, Drag_index, Drag_index, 0, Position, Position, Drag_element_id, Drag_element_id}, {{model, {mouse_dragging, New_state}, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{drag, Position@1}, {mouse_dragging, State}} -> Updated_state = {state, erlang:element(2, State), erlang:element(3, State), erlang:element(4, State) + 1, erlang:element(5, State), Position@1, erlang:element(7, State), erlang:element(8, State)}, {{model, {mouse_dragging, Updated_state}, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{drag_over, Drop_index, Drop_element_id}, {mouse_dragging, State@1}} -> Updated_state@1 = {state, erlang:element(2, State@1), Drop_index, erlang:element(4, State@1), erlang:element(5, State@1), erlang:element(6, State@1), erlang:element(7, State@1), Drop_element_id}, {{model, {mouse_dragging, Updated_state@1}, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{drag_enter, Drop_index@1}, {mouse_dragging, State@2}} -> case (erlang:element(4, State@2) > 1) andalso (erlang:element( 2, State@2 ) /= Drop_index@1) of true -> Equal_groups = groups_equal( erlang:element(4, erlang:element(6, Config)), erlang:element(2, State@2), Drop_index@1, List ), case {Equal_groups, erlang:element(4, Config), erlang:element(2, erlang:element(6, Config))} of {true, on_drag, _} -> Updated_state@2 = update_state_for_operation( erlang:element(5, Config), Drop_index@1, State@2 ), Updated_list = sublist_update( erlang:element(5, Config), erlang:element(2, State@2), Drop_index@1, List ), {{model, {mouse_dragging, Updated_state@2}, erlang:element(3, Model), erlang:element(4, Model)}, (erlang:element(2, Config))( erlang:element(2, State@2), Drop_index@1, Updated_list )}; {false, _, on_drag} -> Updated_state@3 = update_state_for_operation( erlang:element(3, erlang:element(6, Config)), Drop_index@1, State@2 ), Updated_list@1 = groups_list_update( erlang:element(3, erlang:element(6, Config)), erlang:element(4, erlang:element(6, Config)), erlang:element(5, erlang:element(6, Config)), erlang:element(2, State@2), Drop_index@1, List ), {{model, {mouse_dragging, Updated_state@3}, erlang:element(3, Model), erlang:element(4, Model)}, (erlang:element(2, Config))( erlang:element(2, State@2), Drop_index@1, Updated_list@1 )}; {_, _, _} -> Updated_state@4 = {state, erlang:element(2, State@2), erlang:element(3, State@2), 0, erlang:element(5, State@2), erlang:element(6, State@2), erlang:element(7, State@2), erlang:element(8, State@2)}, {{model, {mouse_dragging, Updated_state@4}, erlang:element(3, Model), erlang:element(4, Model)}, List} end; false -> {Model, List} end; {drag_leave, {mouse_dragging, State@3}} -> Updated_state@5 = {state, erlang:element(2, State@3), erlang:element(2, State@3), erlang:element(4, State@3), erlang:element(5, State@3), erlang:element(6, State@3), erlang:element(7, State@3), erlang:element(8, State@3)}, {{model, {mouse_dragging, Updated_state@5}, erlang:element(3, Model), erlang:element(4, Model)}, List}; {drag_end, {mouse_dragging, State@4}} -> case erlang:element(2, State@4) /= erlang:element(3, State@4) of true -> Equal_groups@1 = groups_equal( erlang:element(4, erlang:element(6, Config)), erlang:element(2, State@4), erlang:element(3, State@4), List ), case {Equal_groups@1, erlang:element(4, Config), erlang:element(2, erlang:element(6, Config))} of {true, on_drop, _} -> Updated_list@2 = sublist_update( erlang:element(5, Config), erlang:element(2, State@4), erlang:element(3, State@4), List ), {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, (erlang:element(2, Config))( erlang:element(2, State@4), erlang:element(3, State@4), Updated_list@2 )}; {false, _, on_drop} -> Updated_list@3 = groups_list_update( erlang:element(3, erlang:element(6, Config)), erlang:element(4, erlang:element(6, Config)), erlang:element(5, erlang:element(6, Config)), erlang:element(2, State@4), erlang:element(3, State@4), List ), {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, (erlang:element(2, Config))( erlang:element(2, State@4), erlang:element(3, State@4), Updated_list@3 )}; {_, _, _} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List} end; false -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List} end; {{touch_start, _, _, _}, {touch_pending, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_start, Index, Id, Position@2}, idle} -> Duration_met = erlang:element(10, Config) =:= 0, Pending_state = {touch_pending_state, Index, Id, Position@2, Duration_met}, New_gen = erlang:element(3, Model) + 1, {{model, {touch_pending, Pending_state}, New_gen, true}, List}; {{touch_move, Position@3}, {touch_pending, Pending}} -> case is_within_threshold( erlang:element(4, Pending), Position@3, erlang:element(9, Config) ) of true -> {Model, List}; false -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List} end; {{touch_duration_met, Gen}, {touch_pending, Pending@1}} when Gen =:= erlang:element( 3, Model ) -> Touch_state = {touch_state, erlang:element(2, Pending@1), erlang:element(3, Pending@1), none, false, none}, New_gen@1 = erlang:element(3, Model) + 1, {{model, {touch_selecting, Touch_state}, New_gen@1, erlang:element(4, Model)}, List}; {{touch_duration_met, _}, _} -> {Model, List}; {{touch_drop_cooldown_complete, Gen@1}, {touch_selecting, Touch_state@1}} when Gen@1 =:= erlang:element( 3, Model ) -> Updated_touch_state = {touch_state, erlang:element(2, Touch_state@1), erlang:element(3, Touch_state@1), erlang:element(4, Touch_state@1), true, erlang:element(6, Touch_state@1)}, {{model, {touch_selecting, Updated_touch_state}, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_drop_cooldown_complete, _}, _} -> {Model, List}; {{touch_end, End_position}, {touch_pending, Pending@2}} -> case erlang:element(5, Pending@2) of true -> Within_distance = is_within_threshold( erlang:element(4, Pending@2), End_position, erlang:element(9, Config) ), case Within_distance of true -> Touch_state@2 = {touch_state, erlang:element(2, Pending@2), erlang:element(3, Pending@2), none, true, none}, New_gen@2 = erlang:element(3, Model) + 1, {{model, {touch_selecting, Touch_state@2}, New_gen@2, erlang:element(4, Model)}, List}; false -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List} end; false -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List} end; {{touch_start, _, _, Position@4}, {touch_selecting, Touch_state@3}} -> Updated_touch_state@1 = {touch_state, erlang:element(2, Touch_state@3), erlang:element(3, Touch_state@3), {some, Position@4}, erlang:element(5, Touch_state@3), erlang:element(6, Touch_state@3)}, {{model, {touch_selecting, Updated_touch_state@1}, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_end, End_position@1}, {touch_selecting, Touch_state@4}} -> case erlang:element(4, Touch_state@4) of {some, Start_pos} -> case is_within_threshold( Start_pos, End_position@1, erlang:element(9, Config) ) of true -> Updated_touch_state@2 = {touch_state, erlang:element(2, Touch_state@4), erlang:element(3, Touch_state@4), none, erlang:element(5, Touch_state@4), erlang:element(6, Touch_state@4)}, {{model, {touch_selecting, Updated_touch_state@2}, erlang:element(3, Model), erlang:element(4, Model)}, List}; false -> Updated_touch_state@3 = {touch_state, erlang:element(2, Touch_state@4), erlang:element(3, Touch_state@4), none, erlang:element(5, Touch_state@4), erlang:element(6, Touch_state@4)}, New_gen@3 = erlang:element(3, Model) + 1, {{model, {touch_selecting, Updated_touch_state@3}, New_gen@3, erlang:element(4, Model)}, List} end; none -> {Model, List} end; {{touch_select, Index@1, Id@1}, idle} -> Touch_state@5 = {touch_state, Index@1, Id@1, none, true, none}, New_gen@4 = erlang:element(3, Model) + 1, {{model, {touch_selecting, Touch_state@5}, New_gen@4, erlang:element(4, Model)}, List}; {{touch_drop_start, Position@5}, {touch_selecting, Touch_state@6}} -> Updated_touch_state@4 = {touch_state, erlang:element(2, Touch_state@6), erlang:element(3, Touch_state@6), erlang:element(4, Touch_state@6), erlang:element(5, Touch_state@6), {some, Position@5}}, {{model, {touch_selecting, Updated_touch_state@4}, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_drop_start, _}, _} -> {Model, List}; {{touch_drop, Drop_index@2, End_position@2}, {touch_selecting, Touch_state@7}} -> case {erlang:element(5, Touch_state@7), erlang:element(6, Touch_state@7)} of {false, _} -> {Model, List}; {true, none} -> {Model, List}; {true, {some, Start_position}} -> Within_distance@1 = is_within_threshold( Start_position, End_position@2, erlang:element(9, Config) ), case Within_distance@1 of false -> Updated_touch_state@5 = {touch_state, erlang:element(2, Touch_state@7), erlang:element(3, Touch_state@7), erlang:element(4, Touch_state@7), erlang:element(5, Touch_state@7), none}, {{model, {touch_selecting, Updated_touch_state@5}, erlang:element(3, Model), erlang:element(4, Model)}, List}; true -> case erlang:element(2, Touch_state@7) /= Drop_index@2 of true -> Equal_groups@2 = groups_equal( erlang:element( 4, erlang:element(6, Config) ), erlang:element(2, Touch_state@7), Drop_index@2, List ), case Equal_groups@2 of true -> Updated_list@4 = sublist_update( erlang:element(5, Config), erlang:element( 2, Touch_state@7 ), Drop_index@2, List ), {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, (erlang:element(2, Config))( erlang:element( 2, Touch_state@7 ), Drop_index@2, Updated_list@4 )}; false -> Updated_list@5 = groups_list_update( erlang:element( 3, erlang:element( 6, Config ) ), erlang:element( 4, erlang:element( 6, Config ) ), erlang:element( 5, erlang:element( 6, Config ) ), erlang:element( 2, Touch_state@7 ), Drop_index@2, List ), {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, (erlang:element(2, Config))( erlang:element( 2, Touch_state@7 ), Drop_index@2, Updated_list@5 )} end; false -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List} end end end; {touch_cancel, {touch_pending, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {touch_cancel, {touch_selecting, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_timeout, Gen@2}, {touch_selecting, _}} when Gen@2 =:= erlang:element( 3, Model ) -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_timeout, _}, _} -> {Model, List}; {{touch_start, _, _, _}, {mouse_dragging, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_move, _}, {mouse_dragging, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_end, _}, {mouse_dragging, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_select, _, _}, {mouse_dragging, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{touch_drop, _, _}, {mouse_dragging, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {touch_cancel, {mouse_dragging, _}} -> {{model, idle, erlang:element(3, Model), erlang:element(4, Model)}, List}; {{drag_start, _, _, _}, {touch_pending, _}} -> {Model, List}; {{drag, _}, {touch_pending, _}} -> {Model, List}; {{drag_over, _, _}, {touch_pending, _}} -> {Model, List}; {{drag_enter, _}, {touch_pending, _}} -> {Model, List}; {drag_leave, {touch_pending, _}} -> {Model, List}; {drag_end, {touch_pending, _}} -> {Model, List}; {{drag_start, _, _, _}, {touch_selecting, _}} -> {Model, List}; {{drag, _}, {touch_selecting, _}} -> {Model, List}; {{drag_over, _, _}, {touch_selecting, _}} -> {Model, List}; {{drag_enter, _}, {touch_selecting, _}} -> {Model, List}; {drag_leave, {touch_selecting, _}} -> {Model, List}; {drag_end, {touch_selecting, _}} -> {Model, List}; {_, _} -> {Model, List} end end. -file("src/dnd/groups.gleam", 992). -spec system_update_with_effect(config(NLY), fun((dnd_msg()) -> NMA)) -> fun((dnd_msg(), model(), list(NLY)) -> {model(), list(NLY), lustre@effect:effect(NMA)}). system_update_with_effect(Config, Step_msg) -> Base_update = system_update(Config), fun(Msg, Model, List) -> {New_model, New_list} = Base_update(Msg, Model, List), Generation_changed = erlang:element(3, New_model) /= erlang:element( 3, Model ), Hold_effect = case {erlang:element(2, New_model), Generation_changed} of {{touch_pending, _}, true} when erlang:element(10, Config) > 0 -> schedule_hold_duration( erlang:element(10, Config), erlang:element(3, New_model), Step_msg ); {_, _} -> lustre@effect:none() end, Timeout_effect = case {erlang:element(2, New_model), Generation_changed} of {{touch_selecting, _}, true} when erlang:element(8, Config) > 0 -> schedule_timeout( erlang:element(8, Config), erlang:element(3, New_model), Step_msg ); {_, _} -> lustre@effect:none() end, Cooldown_effect = case {erlang:element(2, New_model), Generation_changed} of {{touch_selecting, Touch_state}, true} when not erlang:element( 5, Touch_state ) andalso (erlang:element(11, Config) > 0) -> schedule_drop_cooldown( erlang:element(11, Config), erlang:element(3, New_model), Step_msg ); {_, _} -> lustre@effect:none() end, {New_model, New_list, lustre@effect:batch([Hold_effect, Timeout_effect, Cooldown_effect])} end. -file("src/dnd/groups.gleam", 399). -spec create(config(NLP), fun((dnd_msg()) -> NLR)) -> system(NLP, NLR). create(Config, Step_msg) -> {system, {model, idle, 0, false}, system_update(Config), drag_events(Step_msg), drop_events(Step_msg), ghost_styles(erlang:element(3, Config)), fun get_info/1, system_update_with_effect(Config, Step_msg), touch_grip_events(Step_msg), touch_drop_zone_events(Step_msg), cancel_events(Step_msg), fun is_touch_selecting/1, fun get_selected_index/1, fun touch_selected_styles/2}.