فهرست منبع

fix(api/menu): fix submenus when created using an object in `items` field in the object passed to `Menu/Submenu.new` (#11441)

* fix(api/menu): fix submenus when created using an object in `items` field in the object passed to `Menu/Submenu.new`

closes #11435

also closes #11422 as I included the docs in this PR

* Update .changes/js-submenu-in-options.md

* Update packages/api/src/menu/base.ts

---------

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
Amr Bashir 9 ماه پیش
والد
کامیت
54cbf59b5a

+ 5 - 0
.changes/js-submenu-in-options.md

@@ -0,0 +1,5 @@
+---
+"@tauri-apps/api": "patch:bug"
+---
+
+Fix submenu created as a menu item instead of a submenu when created by using an object in the `items` field in the options object passed to `Menu.new` or `Submenu.new`.

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 0
crates/tauri/scripts/bundle.global.js


+ 4 - 0
crates/tauri/src/menu/builders/menu.rs

@@ -6,6 +6,10 @@ use crate::{image::Image, menu::*, Manager, Runtime};
 
 /// A builder type for [`Menu`]
 ///
+/// ## Platform-specific:
+///
+/// - **macOS**: if using [`MenuBuilder`] for the global menubar, it can only contain [`Submenu`]s
+///
 /// # Example
 ///
 /// ```no_run

+ 4 - 0
crates/tauri/src/menu/mod.rs

@@ -147,6 +147,10 @@ macro_rules! gen_wrappers {
 gen_wrappers!(
   /// A type that is either a menu bar on the window
   /// on Windows and Linux or as a global menu in the menubar on macOS.
+  ///
+  /// ## Platform-specific:
+  ///
+  /// - **macOS**: if using [`Menu`] for the global menubar, it can only contain [`Submenu`]s
   Menu(MenuInner),
   /// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
   MenuItem(MenuItemInner, MenuItem),

+ 2 - 1
crates/tauri/src/menu/plugin.rs

@@ -300,12 +300,13 @@ impl PredefinedMenuItemPayload {
 
 #[derive(Deserialize)]
 #[serde(untagged)]
+// Note, order matters for untagged enum deserialization
 enum MenuItemPayloadKind {
   ExistingItem((ResourceId, ItemKind)),
   Predefined(PredefinedMenuItemPayload),
   Check(CheckMenuItemPayload),
-  Submenu(SubmenuPayload),
   Icon(IconMenuItemPayload),
+  Submenu(SubmenuPayload),
   MenuItem(MenuItemPayload),
 }
 

+ 23 - 17
packages/api/src/menu/base.ts

@@ -26,11 +26,11 @@ function injectChannel(
     | PredefinedMenuItemOptions
     | CheckMenuItemOptions
 ):
-  | MenuItemOptions
   | SubmenuOptions
-  | IconMenuItemOptions
   | PredefinedMenuItemOptions
-  | (CheckMenuItemOptions & { handler?: Channel<string> }) {
+  | ((MenuItemOptions | IconMenuItemOptions | CheckMenuItemOptions) & {
+      handler?: Channel<string>
+    }) {
   if ('items' in i) {
     i.items = i.items?.map((item) =>
       'rid' in item ? item : injectChannel(item)
@@ -49,14 +49,7 @@ export async function newMenu(
   opts?: unknown
 ): Promise<[number, string]> {
   const handler = new Channel<string>()
-  let items: null | Array<
-    | [number, string]
-    | MenuItemOptions
-    | SubmenuOptions
-    | IconMenuItemOptions
-    | PredefinedMenuItemOptions
-    | CheckMenuItemOptions
-  > = null
+
   if (opts && typeof opts === 'object') {
     if ('action' in opts && opts.action) {
       handler.onmessage = opts.action as () => void
@@ -64,16 +57,21 @@ export async function newMenu(
     }
 
     if ('items' in opts && opts.items) {
-      items = (
-        opts.items as Array<
+      function prepareItem(
+        i:
           | { rid: number; kind: string }
           | MenuItemOptions
           | SubmenuOptions
           | IconMenuItemOptions
           | PredefinedMenuItemOptions
           | CheckMenuItemOptions
-        >
-      ).map((i) => {
+      ):
+        | [number, string]
+        | SubmenuOptions
+        | PredefinedMenuItemOptions
+        | MenuItemOptions
+        | IconMenuItemOptions
+        | CheckMenuItemOptions {
         if ('rid' in i) {
           return [i.rid, i.kind]
         }
@@ -86,14 +84,22 @@ export async function newMenu(
           i.icon = transformImage(i.icon)
         }
 
+        if ('items' in i && i.items) {
+          // @ts-expect-error the `prepareItem` return doesn't exactly match
+          // this is fine, because the difference is in `[number, string]` variant
+          i.items = i.items.map(prepareItem)
+        }
+
         return injectChannel(i)
-      })
+      }
+
+      opts.items = (opts.items as []).map(prepareItem)
     }
   }
 
   return invoke('plugin:menu|new', {
     kind,
-    options: opts ? { ...opts, items } : undefined,
+    options: opts,
     handler
   })
 }

+ 7 - 0
packages/api/src/menu/iconMenuItem.ts

@@ -133,6 +133,13 @@ export enum NativeIcon {
 export interface IconMenuItemOptions extends MenuItemOptions {
   /**
    * Icon to be used for the new icon menu item.
+   *
+   * Note that you may need the `image-ico` or `image-png` Cargo features to use this API.
+   * To enable it, change your Cargo.toml file:
+   * ```toml
+   * [dependencies]
+   * tauri = { version = "...", features = ["...", "image-png"] }
+   * ```
    */
   icon?: NativeIcon | string | Image | Uint8Array | ArrayBuffer | number[]
 }

+ 4 - 0
packages/api/src/menu/menu.ts

@@ -68,6 +68,10 @@ export interface MenuOptions {
 
 /** A type that is either a menu bar on the window
  * on Windows and Linux or as a global menu in the menubar on macOS.
+ *
+ * #### Platform-specific:
+ *
+ * - **macOS**: if using {@linkcode Menu} for the global menubar, it can only contain {@linkcode Submenu}s.
  */
 export class Menu extends MenuItemBase {
   /** @ignore */

+ 2 - 2
packages/api/src/tray.ts

@@ -79,7 +79,7 @@ export interface TrayIconOptions {
   /**
    * The tray icon which could be icon bytes or path to the icon file.
    *
-   * Note that you need the `image-ico` or `image-png` Cargo features to use this API.
+   * Note that you may need the `image-ico` or `image-png` Cargo features to use this API.
    * To enable it, change your Cargo.toml file:
    * ```toml
    * [dependencies]
@@ -196,7 +196,7 @@ export class TrayIcon extends Resource {
   /**
    *  Sets a new tray icon. If `null` is provided, it will remove the icon.
    *
-   * Note that you need the `image-ico` or `image-png` Cargo features to use this API.
+   * Note that you may need the `image-ico` or `image-png` Cargo features to use this API.
    * To enable it, change your Cargo.toml file:
    * ```toml
    * [dependencies]

+ 1 - 1
packages/api/src/window.ts

@@ -1463,7 +1463,7 @@ class Window {
    * await getCurrentWindow().setIcon('/tauri/awesome.png');
    * ```
    *
-   * Note that you need the `image-ico` or `image-png` Cargo features to use this API.
+   * Note that you may need the `image-ico` or `image-png` Cargo features to use this API.
    * To enable it, change your Cargo.toml file:
    * ```toml
    * [dependencies]

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است