diff --git a/addons/mail/static/src/webclient/commands/mail_providers.js b/addons/mail/static/src/webclient/commands/mail_providers.js index 87fd0fbd1b1..bc5e95e4829 100644 --- a/addons/mail/static/src/webclient/commands/mail_providers.js +++ b/addons/mail/static/src/webclient/commands/mail_providers.js @@ -8,7 +8,7 @@ const { Component, xml } = owl; class DialogCommand extends Component {} DialogCommand.template = xml`
- +
`; diff --git a/addons/web/static/src/core/commands/command_items.xml b/addons/web/static/src/core/commands/command_items.xml new file mode 100644 index 00000000000..cf752265f2b --- /dev/null +++ b/addons/web/static/src/core/commands/command_items.xml @@ -0,0 +1,22 @@ + + + + +
+ +
+
+ + +
+ + + + + + + + +
+
+ +
diff --git a/addons/web/static/src/core/commands/command_palette.js b/addons/web/static/src/core/commands/command_palette.js index 7736bb683cd..1b0657a89be 100644 --- a/addons/web/static/src/core/commands/command_palette.js +++ b/addons/web/static/src/core/commands/command_palette.js @@ -1,12 +1,13 @@ /** @odoo-module **/ -import { useAutofocus, useService } from "@web/core/utils/hooks"; import { useHotkey } from "@web/core/hotkeys/hotkey_hook"; +import { _lt } from "@web/core/l10n/translation"; import { KeepLast } from "@web/core/utils/concurrency"; +import { useAutofocus, useService } from "@web/core/utils/hooks"; import { scrollTo } from "@web/core/utils/scrolling"; import { fuzzyLookup } from "@web/core/utils/search"; import { debounce } from "@web/core/utils/timing"; -import { _lt } from "@web/core/l10n/translation"; +import { escapeRegExp } from "../utils/strings"; const { Component, onWillStart, useRef, useState } = owl; @@ -59,6 +60,11 @@ function commandsWithinCategory(categoryName, categories) { }; } +function splitCommandName(name, searchValue) { + const splitName = name.split(new RegExp(`(${escapeRegExp(searchValue)})`, "ig")); + return searchValue.length && splitName.length > 1 ? splitName : [name]; +} + export class DefaultCommandItem extends Component {} DefaultCommandItem.template = "web.DefaultCommandItem"; @@ -175,11 +181,13 @@ export class CommandPalette extends Component { this.state.commands = commands.map((command) => ({ ...command, keyId: this.keyId++, + splitName: splitCommandName(command.name, options.searchValue), })); this.selectCommand(this.state.commands.length ? 0 : -1); this.mouseSelectionActive = false; this.state.emptyMessage = this.emptyMessageByNamespace[namespace] || DEFAULT_EMPTY_MESSAGE.toString(); + this.clearSearchValue = options.searchValue; } selectCommand(index) { diff --git a/addons/web/static/src/core/commands/command_palette.scss b/addons/web/static/src/core/commands/command_palette.scss index 31e59681296..2d09af1b9ee 100644 --- a/addons/web/static/src/core/commands/command_palette.scss +++ b/addons/web/static/src/core/commands/command_palette.scss @@ -91,6 +91,12 @@ } } + .o_command_name { + > b { + color: $o-brand-primary; + } + } + .o_app_icon { position: relative; border-radius: 4%; diff --git a/addons/web/static/src/core/commands/command_palette.xml b/addons/web/static/src/core/commands/command_palette.xml index cd65c01cfeb..c51e3de52d0 100644 --- a/addons/web/static/src/core/commands/command_palette.xml +++ b/addons/web/static/src/core/commands/command_palette.xml @@ -17,7 +17,16 @@
- + + + + + + + + + +
@@ -35,32 +44,4 @@
- -
- -
-
- - -
- - - - - + - - -
-
- - -
- - -
- -
-
-
- diff --git a/addons/web/static/src/webclient/menus/menu_command_item.xml b/addons/web/static/src/webclient/menus/menu_command_item.xml new file mode 100644 index 00000000000..7a62b54da9c --- /dev/null +++ b/addons/web/static/src/webclient/menus/menu_command_item.xml @@ -0,0 +1,14 @@ + + + + +
+ + +
+ +
+
+
+ +
diff --git a/addons/web/static/src/webclient/menus/menu_providers.js b/addons/web/static/src/webclient/menus/menu_providers.js index 3c0bcd62815..188a2446e37 100644 --- a/addons/web/static/src/webclient/menus/menu_providers.js +++ b/addons/web/static/src/webclient/menus/menu_providers.js @@ -2,8 +2,8 @@ import { _lt } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { computeAppsAndMenuItems } from "@web/webclient/menus/menu_helpers"; import { fuzzyLookup } from "@web/core/utils/search"; +import { computeAppsAndMenuItems } from "@web/webclient/menus/menu_helpers"; const { Component } = owl; diff --git a/addons/web/static/tests/core/commands/command_palette_tests.js b/addons/web/static/tests/core/commands/command_palette_tests.js index b0c7605a698..c763e980b15 100644 --- a/addons/web/static/tests/core/commands/command_palette_tests.js +++ b/addons/web/static/tests/core/commands/command_palette_tests.js @@ -1120,3 +1120,91 @@ QUnit.test("navigate in the command palette with an empty list", async (assert) assert.containsNone(target, ".o_command"); assert.containsOnce(target, ".o_command_palette_listbox_empty"); }); + +QUnit.test("bold the searchValue on the commands", async (assert) => { + testComponent = await mount(TestComponent, { env, target }); + const action = () => {}; + const providers = [ + { + namespace: "@", + provide: () => [ + { + name: "Test", + action, + }, + { + name: "test hello", + action, + }, + { + name: "hello test", + action, + }, + { + name: "hello Test hello", + action, + }, + { + name: "TeSt hello Test hello TEST", + action, + }, + ], + }, + ]; + const config = { + searchValue: "@", + providers, + }; + env.services.dialog.add(CommandPaletteDialog, { + config, + }); + await nextTick(); + assert.containsOnce(target, ".o_command_palette"); + assert.containsN(target, ".o_command", 5); + assert.deepEqual( + [...target.querySelectorAll(".o_command b")].map((el) => el.textContent), + [] + ); + + await editSearchBar("@test"); + assert.containsN(target, ".o_command", 5); + assert.deepEqual( + [...target.querySelectorAll(".o_command")].map((command) => { + return [...command.querySelectorAll(".o_command_name b")].map((el) => el.textContent); + }), + [["Test"], ["test"], ["test"], ["Test"], ["TeSt", "Test", "TEST"]] + ); +}); + +QUnit.test("bold the searchValue on the commands with special char", async (assert) => { + testComponent = await mount(TestComponent, { env, target }); + const action = () => {}; + const providers = [ + { + provide: () => [ + { + name: "Test&", + action, + }, + ], + }, + ]; + const config = { + searchValue: "&", + providers, + }; + env.services.dialog.add(CommandPaletteDialog, { + config, + }); + await nextTick(); + assert.containsOnce(target, ".o_command_palette"); + assert.containsN(target, ".o_command", 1); + assert.deepEqual( + [...target.querySelectorAll(".o_command")].map((el) => el.textContent), + ["Test&"] + ); + assert.deepEqual( + [...target.querySelectorAll(".o_command b")].map((el) => el.textContent), + ["&"] + ); +});