diff --git a/examples/components/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/examples/components/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 00000000..13b35eba Binary files /dev/null and b/examples/components/flutter/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/platform/web/melange/core/lui_web_store.ml b/platform/web/melange/core/lui_web_store.ml index 0bcd4e43..5fdefaf3 100644 --- a/platform/web/melange/core/lui_web_store.ml +++ b/platform/web/melange/core/lui_web_store.ml @@ -27,7 +27,7 @@ let insert_at values inserted_index value = invalid_arg "child index is out of bounds"; let rec loop index rest acc = match rest with - | [] -> List.rev_append acc [ value ] |> (fun xs -> xs) + | [] -> List.rev acc | head :: tail -> let acc = if index = inserted_index then value :: acc else acc in loop (index + 1) tail (head :: acc) diff --git a/platform/web/melange/popup/lui_web_menu.ml b/platform/web/melange/popup/lui_web_menu.ml index 97c1319b..20bf0fd3 100644 --- a/platform/web/melange/popup/lui_web_menu.ml +++ b/platform/web/melange/popup/lui_web_menu.ml @@ -471,13 +471,53 @@ let attach_picker_trigger_events_bang = attach_picker_trigger_events (* --- dropdown event wiring --- *) +(* Nested dropdowns are siblings in the DOM (each gets its own positioner in + the portal), so a press inside a child submenu is "outside" the parent menu + and vice versa. The outside-press check has to consider the whole menu + group: walk up through menu-row parents to the root dropdown, then accept a + target inside any positioner in that dropdown's subtree. *) +let rec dropdown_group_root renderer current = + match current.retained_parent with + | Some parent -> ( + match Store.node renderer.web_store parent with + | Some parent_node when Store.menu_item_row parent_node -> ( + match parent_node.retained_parent with + | Some grandparent -> ( + match Store.node renderer.web_store grandparent with + | Some grandparent_node + when Store.standard_kind_is grandparent_node DropdownMenu -> + dropdown_group_root renderer grandparent_node + | _ -> current) + | None -> current) + | _ -> current) + | None -> current + +let rec group_positioners renderer acc current = + let acc = + if Store.standard_kind_is current DropdownMenu then + current.platform_node :: acc + else acc + in + List.fold_left + (fun acc child -> + match Store.node renderer.web_store child with + | Some child_node -> group_positioners renderer acc child_node + | None -> acc) + acc current.retained_children + let dropdown_group_contains_event renderer node event = match Store.node renderer.web_store node with | Some current -> let target = Lui_web_util.event_target_to_element (W.Event.target event) in - W.Element.contains (W.Element.asNode target) current.platform_node + let group_nodes = + group_positioners renderer [] (dropdown_group_root renderer current) + in + List.exists + (fun positioner -> + W.Element.contains (W.Element.asNode target) positioner) + group_nodes || (match current.retained_parent with | Some parent -> @@ -606,8 +646,11 @@ let attach_dropdown_events renderer node _dropdown_node = | None -> ()) in let pointer_handler event = - if not (dropdown_group_contains_event renderer node event) then - emit renderer (Dismiss node); + let menu_node = Lui_web_nodes.dom_node renderer node in + if + W.Element.hasAttribute "data-open" menu_node + && not (dropdown_group_contains_event renderer node event) + then emit renderer (Dismiss node); refresh_position event in let key_handler event = diff --git a/platform/web/src/lui.css b/platform/web/src/lui.css index 3b8f19ce..ecb4bf85 100644 --- a/platform/web/src/lui.css +++ b/platform/web/src/lui.css @@ -15,7 +15,7 @@ --accent-foreground: oklch(0.205 0 0); --muted-foreground: oklch(0.556 0 0); --destructive: oklch(0.577 0.245 27.325); - --destructive-foreground: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.985 0 0); --error: oklch(0.93 0.0314 17.73); --error-foreground: oklch(0.64 0.2082 25.38); --success: oklch(0.95 0.0506 162.83); @@ -574,6 +574,7 @@ body { .lui-modal-layer { @apply fixed inset-0 isolate; z-index: 1000; + pointer-events: auto; } .lui-modal-layer[data-ending-style] {