mirror of
https://github.com/penpot/penpot.git
synced 2026-07-31 18:36:18 +00:00
♻️ Fixes on shortcuts (#10906)
* 🎉 Improve reset to default flow * 🎉 Add diff modal * 🐛 Fix measurement shortcuts * 🎉 Separate open-sections for each tab * 🐛 Align button link icon * 🎉 Reset only works after press save * 🐛 Fix little things * 🎉 Make the import export row to be fixed * ♻️ Fix CI * 🐛 Cancel shortcut is not appearing on disabled tab * 🐛 Fix tests * 🐛 Fix loop on personalized * 🐛 Fix show measurements shortcut
This commit is contained in:
parent
370e91c3e3
commit
fe85a5717e
@ -305,6 +305,13 @@ export class ShortcutsPage extends BaseWebSocketPage {
|
||||
});
|
||||
}
|
||||
|
||||
async confirmImportApply() {
|
||||
const applyButton = this.page.getByRole("button", {
|
||||
name: /apply/i,
|
||||
});
|
||||
await applyButton.click();
|
||||
}
|
||||
|
||||
async getExportedJson() {
|
||||
const [download] = await Promise.all([
|
||||
this.page.waitForEvent("download"),
|
||||
|
||||
@ -77,10 +77,12 @@ test.describe("Shortcut Import", () => {
|
||||
workspace: { "align-bottom": "ctrl+y" },
|
||||
});
|
||||
|
||||
await shortcutsPage.confirmImportApply();
|
||||
|
||||
await shortcutsPage.expectShortcutCustomized("Align bottom");
|
||||
|
||||
await shortcutsPage.searchForShortcut("Align bottom");
|
||||
await shortcutsPage.clickTab("Personalized");
|
||||
await shortcutsPage.searchForShortcut("Align bottom");
|
||||
await shortcutsPage.expectShortcutVisible("Align bottom");
|
||||
});
|
||||
|
||||
@ -116,6 +118,8 @@ test.describe("Shortcut Import", () => {
|
||||
dashboard: { "toggle-theme": "alt+m" },
|
||||
});
|
||||
|
||||
await shortcutsPage.confirmImportApply();
|
||||
|
||||
await expect(
|
||||
page.getByRole("alert").filter({ hasText: /Invalid data/i }),
|
||||
).not.toBeVisible();
|
||||
@ -144,6 +148,8 @@ test.describe("Shortcut Import", () => {
|
||||
},
|
||||
});
|
||||
|
||||
await shortcutsPage.confirmImportApply();
|
||||
|
||||
await shortcutsPage.expectShortcutCustomized("Align bottom");
|
||||
|
||||
await shortcutsPage.expandSubsection("Alignment");
|
||||
@ -183,6 +189,8 @@ test.describe("Shortcut Export", () => {
|
||||
workspace: { "align-bottom": "" },
|
||||
});
|
||||
|
||||
await shortcutsPage.confirmImportApply();
|
||||
|
||||
await shortcutsPage.searchForShortcut("Align bottom");
|
||||
await shortcutsPage.expectShortcutDisabled("Align bottom");
|
||||
|
||||
@ -228,11 +236,36 @@ test.describe("Shortcut Reset", () => {
|
||||
await shortcutsPage.importShortcuts({
|
||||
workspace: { "align-bottom": "ctrl+y" },
|
||||
});
|
||||
await shortcutsPage.confirmImportApply();
|
||||
await shortcutsPage.expectShortcutCustomized("Align bottom");
|
||||
|
||||
await shortcutsPage.restoreAllShortcuts();
|
||||
await shortcutsPage.expectShortcutNotCustomized("Align bottom");
|
||||
});
|
||||
|
||||
test("Reset button shows pending message and requires save to apply", async ({
|
||||
page,
|
||||
}) => {
|
||||
const shortcutsPage = new ShortcutsPage(page);
|
||||
await shortcutsPage.goToShortcuts();
|
||||
|
||||
await shortcutsPage.expandSubsection("Alignment");
|
||||
await shortcutsPage.clickEditShortcut("Align bottom");
|
||||
await shortcutsPage.pressKey("Control+y");
|
||||
await shortcutsPage.saveShortcut();
|
||||
await shortcutsPage.expectShortcutCustomized("Align bottom");
|
||||
|
||||
await shortcutsPage.clickEditShortcut("Align bottom");
|
||||
await shortcutsPage.resetShortcut("Align bottom");
|
||||
|
||||
await expect(
|
||||
page.getByText(/If you save, this shortcut will return/i),
|
||||
).toBeVisible();
|
||||
|
||||
await shortcutsPage.saveShortcut();
|
||||
|
||||
await shortcutsPage.expectShortcutNotCustomized("Align bottom");
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Shortcut Persistence", () => {
|
||||
@ -315,6 +348,7 @@ test.describe("Shortcut Round-Trip", () => {
|
||||
await shortcutsPage.expectShortcutNotCustomized("Align bottom");
|
||||
|
||||
await shortcutsPage.importShortcuts(exported);
|
||||
await shortcutsPage.confirmImportApply();
|
||||
await shortcutsPage.expectShortcutCustomized("Align bottom");
|
||||
|
||||
const reExported = await shortcutsPage.getExportedJson();
|
||||
|
||||
@ -207,13 +207,16 @@
|
||||
(remove #(:disabled (second %)))
|
||||
(run! (fn [[key {:keys [command fn type overwrite]}]]
|
||||
(let [callback (wrap-cb key fn)
|
||||
undefined (js* "(void 0)")
|
||||
commands (if (vector? command)
|
||||
(into-array command)
|
||||
#js [command])]
|
||||
(if type
|
||||
(mousetrap/bind commands callback type overwrite)
|
||||
(mousetrap/bind commands callback undefined overwrite)))))))
|
||||
(if (vector? type)
|
||||
(do (mousetrap/bind commands callback (nth type 0) overwrite)
|
||||
(mousetrap/bind commands callback (nth type 1) overwrite))
|
||||
(let [undefined (js* "(void 0)")]
|
||||
(if type
|
||||
(mousetrap/bind commands callback type overwrite)
|
||||
(mousetrap/bind commands callback undefined overwrite)))))))))
|
||||
|
||||
(defn- reset!
|
||||
([]
|
||||
|
||||
@ -34,38 +34,46 @@
|
||||
(def shortcuts
|
||||
{:escape {:tooltip (ds/esc)
|
||||
:command ["escape" "enter" "v"]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (esc-pressed))}
|
||||
|
||||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dwu/undo)}
|
||||
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dwu/redo)}
|
||||
|
||||
;; ZOOM
|
||||
|
||||
:increase-zoom {:tooltip "+"
|
||||
:command "+"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (dw/increase-zoom nil))}
|
||||
|
||||
:decrease-zoom {:tooltip "-"
|
||||
:command "-"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (dw/decrease-zoom nil))}
|
||||
|
||||
:reset-zoom {:tooltip (ds/shift "0")
|
||||
:command "shift+0"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dw/reset-zoom)}
|
||||
|
||||
:fit-all {:tooltip (ds/shift "1")
|
||||
:command "shift+1"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dw/zoom-to-fit-all)}
|
||||
|
||||
:zoom-selected {:tooltip (ds/shift "2")
|
||||
:command "shift+2"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dw/zoom-to-selected-shape)}})
|
||||
|
||||
(defn get-tooltip [shortcut]
|
||||
(assert (contains? shortcuts shortcut) (str shortcut))
|
||||
(get-in shortcuts [shortcut :tooltip]))
|
||||
(get-in shortcuts [shortcut :tooltip]))
|
||||
|
||||
@ -30,120 +30,146 @@
|
||||
{:move-nodes {:tooltip "M"
|
||||
:command "m"
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/change-edit-mode :move))}
|
||||
|
||||
:draw-nodes {:tooltip "P"
|
||||
:command "p"
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/change-edit-mode :draw))}
|
||||
|
||||
:add-node {:tooltip (ds/shift "+")
|
||||
:command "shift++"
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/add-node))}
|
||||
|
||||
:delete-node {:tooltip (ds/supr)
|
||||
:command ["del" "backspace"]
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/remove-node))}
|
||||
|
||||
:merge-nodes {:tooltip (ds/meta "J")
|
||||
:command (ds/c-mod "j")
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/merge-nodes))}
|
||||
|
||||
:join-nodes {:tooltip "J"
|
||||
:command "j"
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/join-nodes))}
|
||||
|
||||
:separate-nodes {:tooltip "K"
|
||||
:command "k"
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/separate-nodes))}
|
||||
|
||||
:make-corner {:tooltip "X"
|
||||
:command "x"
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/make-corner))}
|
||||
|
||||
:make-curve {:tooltip "C"
|
||||
:command "c"
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/make-curve))}
|
||||
|
||||
:snap-nodes {:tooltip (ds/meta "'")
|
||||
;;https://github.com/ccampbell/mousetrap/issues/85
|
||||
:command [(ds/c-mod "'") (ds/c-mod "219")]
|
||||
:subsections [:path-editor]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/toggle-snap))}
|
||||
|
||||
:escape {:tooltip (ds/esc)
|
||||
:command ["escape" "enter" "v"]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (esc-pressed))}
|
||||
|
||||
:undo {:tooltip (ds/meta "Z")
|
||||
:command (ds/c-mod "z")
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/undo-path))}
|
||||
|
||||
:redo {:tooltip (ds/meta "Y")
|
||||
:command [(ds/c-mod "shift+z") (ds/c-mod "y")]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/redo-path))}
|
||||
|
||||
;; ZOOM
|
||||
|
||||
:increase-zoom {:tooltip "+"
|
||||
:command "+"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (dw/increase-zoom nil))}
|
||||
|
||||
:decrease-zoom {:tooltip "-"
|
||||
:command "-"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (dw/decrease-zoom nil))}
|
||||
|
||||
:reset-zoom {:tooltip (ds/shift "0")
|
||||
:command "shift+0"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dw/reset-zoom)}
|
||||
|
||||
:fit-all {:tooltip (ds/shift "1")
|
||||
:command "shift+1"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dw/zoom-to-fit-all)}
|
||||
|
||||
:zoom-selected {:tooltip (ds/shift "2")
|
||||
:command "shift+2"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! dw/zoom-to-selected-shape)}
|
||||
|
||||
;; Arrow movement
|
||||
|
||||
:move-fast-up {:tooltip (ds/shift ds/up-arrow)
|
||||
:command "shift+up"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :up true))}
|
||||
|
||||
:move-fast-down {:tooltip (ds/shift ds/down-arrow)
|
||||
:command "shift+down"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :down true))}
|
||||
|
||||
:move-fast-right {:tooltip (ds/shift ds/right-arrow)
|
||||
:command "shift+right"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :right true))}
|
||||
|
||||
:move-fast-left {:tooltip (ds/shift ds/left-arrow)
|
||||
:command "shift+left"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :left true))}
|
||||
|
||||
:move-unit-up {:tooltip ds/up-arrow
|
||||
:command "up"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :up false))}
|
||||
|
||||
:move-unit-down {:tooltip ds/down-arrow
|
||||
:command "down"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :down false))}
|
||||
|
||||
:move-unit-left {:tooltip ds/right-arrow
|
||||
:command "right"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :right false))}
|
||||
|
||||
:move-unit-right {:tooltip ds/left-arrow
|
||||
:command "left"
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! (drp/move-selected :left false))}})
|
||||
|
||||
(defn get-tooltip [shortcut]
|
||||
|
||||
@ -148,23 +148,19 @@
|
||||
:section [:workspace]
|
||||
:fn #(emit-when-no-readonly (dw/start-editing-selected))}
|
||||
|
||||
:start-measure {:tooltip (ds/alt "")
|
||||
:command ["alt" "."]
|
||||
:type "keydown"
|
||||
:subsections [:edit]
|
||||
:section [:workspace]
|
||||
:fn #(emit-when-no-readonly (dw/toggle-distances-display true))}
|
||||
|
||||
:stop-measure {:tooltip (ds/alt "")
|
||||
:command ["alt" "."]
|
||||
:type "keyup"
|
||||
:subsections [:edit]
|
||||
:section [:workspace]
|
||||
:fn #(emit-when-no-readonly (dw/toggle-distances-display false))}
|
||||
:show-measure {:tooltip (ds/alt "")
|
||||
:command ["alt" "."]
|
||||
:type ["keydown" "keyup"]
|
||||
:subsections [:tools]
|
||||
:section [:workspace]
|
||||
:fn #(emit-when-no-readonly
|
||||
(let [type (.-type %)]
|
||||
(dw/toggle-distances-display (if (= type "keydown") true false))))}
|
||||
|
||||
:escape {:tooltip (ds/esc)
|
||||
:command "escape"
|
||||
:subsections [:edit]
|
||||
:section [:workspace]
|
||||
:fn #(st/emit! :interrupt (dwdc/clear-drawing) (dw/deselect-all true))}
|
||||
|
||||
:find {:tooltip (ds/meta "F") :command (ds/c-mod "f") :subsections [:edit]
|
||||
@ -191,6 +187,7 @@
|
||||
:ungroup {:tooltip (ds/shift "G")
|
||||
:command "shift+g"
|
||||
:subsections [:modify-layers]
|
||||
:section [:workspace]
|
||||
:fn #(emit-when-no-readonly (dw/ungroup-selected))}
|
||||
|
||||
:mask {:tooltip (ds/meta "M")
|
||||
@ -363,11 +360,13 @@
|
||||
:draw-line {:tooltip "L"
|
||||
:command "l"
|
||||
:subsections [:tools]
|
||||
:section [:workspace]
|
||||
:fn #(emit-when-no-readonly (dwd/select-for-drawing :line))}
|
||||
|
||||
:draw-arrow {:tooltip (ds/shift "L")
|
||||
:command "shift+l"
|
||||
:subsections [:tools]
|
||||
:section [:workspace]
|
||||
:fn #(emit-when-no-readonly (dwd/select-for-drawing :arrow))}
|
||||
|
||||
:draw-curve {:tooltip (ds/shift "C")
|
||||
|
||||
@ -23,9 +23,9 @@
|
||||
transition: all 0.4s;
|
||||
text-decoration: none !important;
|
||||
block-size: $sz-40;
|
||||
|
||||
.logo {
|
||||
block-size: px2rem(20);
|
||||
inline-size: px2rem(20);
|
||||
}
|
||||
}
|
||||
|
||||
.logo {
|
||||
block-size: px2rem(20);
|
||||
inline-size: px2rem(20);
|
||||
}
|
||||
|
||||
@ -0,0 +1,161 @@
|
||||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) KALEIDOS INC Sucursal en España SL
|
||||
|
||||
(ns app.main.ui.settings.import-shortcuts-diff-modal
|
||||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.i18n :refer [tr]]
|
||||
[app.main.data.dashboard.shortcuts :as dsc]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.viewer.shortcuts :as vsc]
|
||||
[app.main.data.workspace.path.shortcuts :as psc]
|
||||
[app.main.data.workspace.shortcuts :as wsc]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||
[app.main.ui.ds.foundations.assets.icon :as i]
|
||||
[app.main.ui.shortcuts :as ss]
|
||||
[app.util.dom :as dom]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(def ^:private context->defaults
|
||||
{:workspace (d/deep-merge psc/shortcuts wsc/shortcuts)
|
||||
:dashboard dsc/shortcuts
|
||||
:viewer vsc/shortcuts})
|
||||
|
||||
(defn compute-diff
|
||||
[imported-shortcuts custom-shortcuts]
|
||||
(let [contexts (filter #(contains? imported-shortcuts %) [:workspace :dashboard :viewer])]
|
||||
(mapcat
|
||||
(fn [ctx]
|
||||
(let [imported-ctx (get imported-shortcuts ctx)
|
||||
current-ctx (get custom-shortcuts ctx {})
|
||||
defaults-ctx (get context->defaults ctx)]
|
||||
(keep
|
||||
(fn [[shortcut-key imported-binding]]
|
||||
(let [current-binding (get current-ctx shortcut-key)
|
||||
default-cmd (:command (get defaults-ctx shortcut-key))
|
||||
effective-binding (or current-binding default-cmd)
|
||||
customized? (and (some? current-binding)
|
||||
(seq current-binding)
|
||||
(not= current-binding default-cmd))]
|
||||
(when (and (some? imported-binding)
|
||||
(not= effective-binding imported-binding))
|
||||
{:context ctx
|
||||
:key shortcut-key
|
||||
:current effective-binding
|
||||
:imported imported-binding
|
||||
:customized? customized?})))
|
||||
imported-ctx)))
|
||||
contexts)))
|
||||
|
||||
(def ^:private context-order {:workspace 0 :dashboard 1 :viewer 2})
|
||||
|
||||
(def ^:private context-name
|
||||
{:workspace "Workspace"
|
||||
:dashboard "Dashboard"
|
||||
:viewer "Viewer"})
|
||||
|
||||
(mf/defc import-shortcuts-diff-modal
|
||||
{::mf/register modal/components
|
||||
::mf/register-as :import-shortcuts-diff-modal}
|
||||
[{:keys [imported-shortcuts custom-shortcuts all-shortcuts-raw]}]
|
||||
(let [diff-entries
|
||||
(mf/with-memo [imported-shortcuts custom-shortcuts]
|
||||
(->> (compute-diff imported-shortcuts custom-shortcuts)
|
||||
(sort-by (fn [e] [(get context-order (:context e) 99)
|
||||
(name (:key e))]))))
|
||||
|
||||
handle-close-dialog
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (modal/hide))))
|
||||
|
||||
handle-apply
|
||||
(mf/use-fn
|
||||
(mf/deps imported-shortcuts all-shortcuts-raw)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (ss/import-custom-shortcuts imported-shortcuts all-shortcuts-raw))
|
||||
(st/emit! (modal/hide))))]
|
||||
|
||||
[:div {:class (stl/css :modal-overlay)}
|
||||
[:div {:class (stl/css :modal-dialog)}
|
||||
[:> icon-button* {:class (stl/css :close-btn)
|
||||
:variant "ghost"
|
||||
:aria-label (tr "labels.close")
|
||||
:on-click handle-close-dialog
|
||||
:tooltip-class (stl/css :close-btn-tooltip)
|
||||
:icon i/close}]
|
||||
[:div {:class (stl/css :modal-title)}
|
||||
(tr "import-shortcuts.diff-modal-title")]
|
||||
|
||||
[:div {:class (stl/css :modal-content)}
|
||||
[:div {:class (stl/css :modal-content-text)}
|
||||
(tr "import-shortcuts.diff-modal-text")]
|
||||
|
||||
(if (seq diff-entries)
|
||||
[:table {:class (stl/css :shortcuts-table)}
|
||||
[:thead
|
||||
[:tr {:class (stl/css :shortcuts-list-header)}
|
||||
[:th {:class (stl/css :shortcut-header-name)}
|
||||
(tr "restore-shortcuts.acction")]
|
||||
[:th {:class (stl/css :shortcut-header-command)}
|
||||
(tr "labels.current")]
|
||||
[:th {:class (stl/css :shortcut-header-command)}
|
||||
(tr "labels.import")]]]
|
||||
[:tbody {:class (stl/css :shortcuts-list-body)}
|
||||
(let [last-ctx* (volatile! nil)]
|
||||
(for [entry diff-entries]
|
||||
(let [{:keys [context key current imported customized?]} entry
|
||||
show-context-label? (not= @last-ctx* context)]
|
||||
(vreset! last-ctx* context)
|
||||
[:* {:key (dm/str (name context) "-" (name key) "-group")}
|
||||
(when show-context-label?
|
||||
[:tr {:key (dm/str "ctx-" (name context))
|
||||
:class (stl/css :context-separator)}
|
||||
[:td {:colSpan 3
|
||||
:class (stl/css :context-label)}
|
||||
(get context-name context)]])
|
||||
[:tr {:key (dm/str (name context) "-" (name key))
|
||||
:class (stl/css :shortcuts-list-item)}
|
||||
[:td {:class (stl/css :shortcut-name)}
|
||||
(ss/translation-keyname :sc key)]
|
||||
[:td {:class (stl/css :shortcut-command)}
|
||||
(if (str/blank? current)
|
||||
[:span {:class (stl/css :shortcut-empty)} "-"]
|
||||
[:> ss/shortcuts-keys* {:content current
|
||||
:command key
|
||||
:is-customized customized?
|
||||
:light-shortcut true
|
||||
:has-conflict? false}])]
|
||||
[:td {:class (stl/css :shortcut-command)}
|
||||
(if (str/blank? imported)
|
||||
[:span {:class (stl/css :shortcut-empty)} "-"]
|
||||
[:> ss/shortcuts-keys* {:content imported
|
||||
:command key
|
||||
:light-shortcut true
|
||||
:is-customized true
|
||||
:has-conflict? false}])]]])))]]
|
||||
[:div {:class (stl/css :no-changes)}
|
||||
(tr "import-shortcuts.no-changes")])]
|
||||
|
||||
[:div {:class (stl/css :modal-footer)}
|
||||
[:div {:class (stl/css :action-buttons)}
|
||||
[:> button* {:class (stl/css :cancel-button)
|
||||
:variant "secondary"
|
||||
:type "button"
|
||||
:on-click handle-close-dialog}
|
||||
(tr "labels.cancel")]
|
||||
[:> button* {:class (stl/css :cancel-button)
|
||||
:variant "primary"
|
||||
:type "button"
|
||||
:on-click handle-apply}
|
||||
(tr "import-shortcuts.apply")]]]]]))
|
||||
@ -0,0 +1,167 @@
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC Sucursal en España SL
|
||||
|
||||
@use "ds/typography.scss" as t;
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/spacing.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/_utils.scss" as *;
|
||||
|
||||
.modal-overlay {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
position: fixed;
|
||||
inset-inline-start: 0;
|
||||
inset-block-start: 0;
|
||||
block-size: 100%;
|
||||
inline-size: 100%;
|
||||
z-index: var(--z-index-set);
|
||||
background-color: var(--overlay-color);
|
||||
padding-inline-start: px2rem(272);
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
position: relative;
|
||||
padding: var(--sp-xxxl);
|
||||
border-radius: $br-8;
|
||||
background-color: var(--color-background-primary);
|
||||
border: $b-2 solid var(--color-background-quaternary);
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
gap: var(--sp-xxxl);
|
||||
min-inline-size: $sz-364;
|
||||
min-block-size: $sz-192;
|
||||
max-block-size: $sz-520;
|
||||
inline-size: px2rem(584);
|
||||
max-inline-size: $sz-712;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
@include t.use-typography("headline-medium");
|
||||
|
||||
color: var(--color-foreground-primary);
|
||||
block-size: px2rem(26);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
inset-block-start: px2rem(8);
|
||||
inset-inline-end: px2rem(6);
|
||||
block-size: $sz-32;
|
||||
inline-size: $sz-28;
|
||||
}
|
||||
|
||||
.close-btn-tooltip {
|
||||
position: absolute;
|
||||
inset-block-start: px2rem(8);
|
||||
inset-inline-end: px2rem(6);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-l);
|
||||
block-size: fit-content;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.modal-content-text {
|
||||
@include t.use-typography("body-medium");
|
||||
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
justify-self: end;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.action-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.shortcuts-table {
|
||||
inline-size: 100%;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.shortcuts-list-item,
|
||||
.shortcuts-list-header {
|
||||
@include t.use-typography("body-medium");
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
display: grid;
|
||||
grid-template-columns: 35% 30% 35%;
|
||||
}
|
||||
|
||||
.shortcuts-list-header {
|
||||
border-block-end: $b-1 solid var(--color-foreground-secondary);
|
||||
block-size: $sz-32;
|
||||
text-align: start;
|
||||
padding-block-end: var(--sp-s);
|
||||
}
|
||||
|
||||
.shortcuts-list-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--sp-s);
|
||||
}
|
||||
|
||||
.shortcuts-list-item {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.shortcuts-list-item:first-child {
|
||||
padding-block-start: var(--sp-s);
|
||||
}
|
||||
|
||||
.shortcut-header-name,
|
||||
.shortcut-header-command {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.shortcut-name {
|
||||
text-align: start;
|
||||
}
|
||||
|
||||
.shortcut-command {
|
||||
display: flex;
|
||||
gap: var(--sp-s);
|
||||
align-items: center;
|
||||
justify-content: start;
|
||||
text-align: start;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.shortcut-empty {
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.context-separator {
|
||||
border-block-end: none;
|
||||
}
|
||||
|
||||
.context-label {
|
||||
@include t.use-typography("title-small");
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
text-align: start;
|
||||
padding-block: var(--sp-m) var(--sp-xxs);
|
||||
}
|
||||
|
||||
.no-changes {
|
||||
@include t.use-typography("body-medium");
|
||||
|
||||
color: var(--color-foreground-secondary);
|
||||
text-align: center;
|
||||
padding-block: var(--sp-xl);
|
||||
}
|
||||
@ -46,7 +46,9 @@
|
||||
(drop-last default-chars-list))
|
||||
|
||||
ctx-customs (get custom-shortcuts context {})
|
||||
current-command (or (get ctx-customs shortcut-key) default-command)
|
||||
custom-val (get ctx-customs shortcut-key)
|
||||
current-command (or custom-val default-command)
|
||||
customized? (and (some? custom-val) (seq custom-val) (not= custom-val default-command))
|
||||
|
||||
current-managed-list (if (coll? current-command)
|
||||
current-command
|
||||
@ -59,7 +61,7 @@
|
||||
current-short-char-list (if (= 1 (count current-chars-list))
|
||||
current-chars-list
|
||||
(drop-last current-chars-list))]
|
||||
[default-last-element default-short-char-list current-last-element current-command current-short-char-list]))
|
||||
[default-last-element default-short-char-list current-last-element current-command current-short-char-list customized?]))
|
||||
|
||||
(mf/defc restore-all-modal
|
||||
{::mf/register modal/components
|
||||
@ -107,9 +109,11 @@
|
||||
default-short-char-list
|
||||
current-last-element
|
||||
current-command
|
||||
current-short-char-list] (extract-shortcut-keys shortcut-key custom-shortcuts context)
|
||||
current-short-char-list
|
||||
customized?] (extract-shortcut-keys shortcut-key custom-shortcuts context)
|
||||
current-penultimate (last current-short-char-list)
|
||||
default-penultimate (last default-short-char-list)]
|
||||
default-penultimate (last default-short-char-list)
|
||||
command-class (if customized? (stl/css :customized-command) (stl/css :default-command))]
|
||||
[:tr {:key (dm/str (name context) "-" (name shortcut-key))
|
||||
:class (stl/css :shortcuts-list-item)}
|
||||
[:td {:class (stl/css :shortcut-name)}
|
||||
@ -122,7 +126,7 @@
|
||||
(for [char chars]
|
||||
[:> ss/converted-chars* {:key (dm/str char "-" (name shortcut-key))
|
||||
:char char
|
||||
:class (stl/css :default-command)
|
||||
:class command-class
|
||||
:command shortcut-key}])
|
||||
(when (not= chars current-penultimate) [:span {:class (stl/css :space)} ","])]))
|
||||
(when (not= current-last-element current-penultimate)
|
||||
@ -131,7 +135,7 @@
|
||||
(for [char current-last-element]
|
||||
[:> ss/converted-chars* {:key (dm/str char "-" (name shortcut-key))
|
||||
:char char
|
||||
:class (stl/css :default-command)
|
||||
:class command-class
|
||||
:command shortcut-key}])])]
|
||||
|
||||
[:td {:class (stl/css :shortcut-command)}
|
||||
|
||||
@ -21,6 +21,7 @@
|
||||
inline-size: 100%;
|
||||
z-index: var(--z-index-set);
|
||||
background-color: var(--overlay-color);
|
||||
padding-inline-start: px2rem(272);
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
@ -142,6 +143,12 @@
|
||||
border-radius: $br-6;
|
||||
}
|
||||
|
||||
.customized-command {
|
||||
background-color: var(--color-background-info);
|
||||
color: var(--color-foreground-primary);
|
||||
border-radius: $br-6;
|
||||
}
|
||||
|
||||
.shortcut-empty {
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@
|
||||
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
||||
[app.main.ui.ds.product.empty-state :refer [empty-state*]]
|
||||
[app.main.ui.settings.import-shortcuts-diff-modal]
|
||||
[app.main.ui.settings.restore-shortcuts-modal]
|
||||
[app.main.ui.shortcuts :as ss]
|
||||
[app.util.dom :as dom]
|
||||
@ -35,7 +36,7 @@
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc search-section*
|
||||
[{:keys [filter-term on-search-term-change on-search-clear-click on-restore-all show-restore-all? has-custom-shortcuts]}]
|
||||
[{:keys [filter-term on-search-term-change on-search-clear-click on-restore-all show-restore-all has-custom-shortcuts]}]
|
||||
[:div {:class (stl/css :shortcuts-search-section)}
|
||||
[:> search-bar* {:on-change on-search-term-change
|
||||
:on-clear on-search-clear-click
|
||||
@ -43,7 +44,7 @@
|
||||
:placeholder (tr "shortcuts.title")
|
||||
:icon-id i/search
|
||||
:auto-focus true}]
|
||||
(when (and show-restore-all? has-custom-shortcuts)
|
||||
(when (and show-restore-all has-custom-shortcuts)
|
||||
[:> button*
|
||||
{:variant "secondary"
|
||||
:on-click on-restore-all
|
||||
@ -171,10 +172,9 @@
|
||||
:conflicts conflicts}]))]))
|
||||
|
||||
(mf/defc shortcuts-tab-section*
|
||||
[{:keys [shortcut-filter show-restore-all? on-restore-all empty-str custom-shortcuts conflicts]}]
|
||||
[{:keys [shortcut-filter show-restore-all on-restore-all empty-str custom-shortcuts conflicts expand-all-by-default
|
||||
open-sections update-open-sections]}]
|
||||
(let [{:keys [all-shortcuts]} (mf/use-ctx ctx/shortcuts-ctx)
|
||||
open-sections* (mf/use-state [[:workspace]])
|
||||
open-sections (deref open-sections*)
|
||||
|
||||
filter-term* (mf/use-state "")
|
||||
filter-term (deref filter-term*)
|
||||
@ -186,8 +186,15 @@
|
||||
(seq (:children (:none children)))
|
||||
(seq children))))
|
||||
|
||||
all-shortcuts (into {} (filter (fn [[_ v]] (section-has-content? v)) all-shortcuts))
|
||||
filtered-shortcuts (filter-shortcuts-tree all-shortcuts shortcut-filter filter-term)
|
||||
all-shortcuts (mf/with-memo [all-shortcuts]
|
||||
(into {} (filter (fn [[_ v]] (section-has-content? v)) all-shortcuts)))
|
||||
filtered-shortcuts (mf/with-memo [all-shortcuts shortcut-filter filter-term]
|
||||
(filter-shortcuts-tree all-shortcuts shortcut-filter filter-term))
|
||||
|
||||
expand-all-sections
|
||||
(fn []
|
||||
(let [all-ids (collect-open-section-ids filtered-shortcuts)]
|
||||
(update-open-sections (if (seq all-ids) (vec all-ids) [[:workspace]]))))
|
||||
|
||||
on-search-term-change
|
||||
(mf/use-fn
|
||||
@ -195,16 +202,20 @@
|
||||
(fn [term]
|
||||
(reset! filter-term* term)
|
||||
(if (str/blank? term)
|
||||
(reset! open-sections* [[:workspace]])
|
||||
(if expand-all-by-default
|
||||
(expand-all-sections)
|
||||
(update-open-sections [[:workspace]]))
|
||||
(let [filtered-tree (filter-shortcuts-tree all-shortcuts shortcut-filter term)
|
||||
open-ids (collect-open-section-ids filtered-tree)]
|
||||
(reset! open-sections* open-ids)))))
|
||||
(update-open-sections (vec open-ids))))))
|
||||
|
||||
on-search-clear-click
|
||||
(mf/use-fn
|
||||
(fn [_]
|
||||
(reset! open-sections* [[:workspace]])
|
||||
(reset! filter-term* "")))
|
||||
(reset! filter-term* "")
|
||||
(if expand-all-by-default
|
||||
(expand-all-sections)
|
||||
(update-open-sections [[:workspace]]))))
|
||||
|
||||
manage-sections
|
||||
(fn [item]
|
||||
@ -214,15 +225,28 @@
|
||||
new-value (if is-present?
|
||||
(filterv (fn [element] (not= element item)) open-sections)
|
||||
(conj open-sections item))]
|
||||
(reset! open-sections* new-value))))]
|
||||
(update-open-sections new-value))))
|
||||
|
||||
has-custom-shortcuts (some #(seq (val %)) custom-shortcuts)
|
||||
|
||||
empty-str
|
||||
(if (and has-custom-shortcuts (not (str/blank? filter-term)))
|
||||
(tr "shortcuts.no-shortcuts")
|
||||
empty-str)]
|
||||
|
||||
(mf/with-effect [filtered-shortcuts filter-term expand-all-by-default open-sections]
|
||||
(when (and expand-all-by-default (str/blank? filter-term))
|
||||
(let [all-ids (vec (collect-open-section-ids filtered-shortcuts))]
|
||||
(when (and (seq all-ids) (not= all-ids open-sections))
|
||||
(update-open-sections all-ids)))))
|
||||
|
||||
[:div {:class (stl/css :shortcuts-section)}
|
||||
[:> search-section* {:filter-term filter-term
|
||||
:on-search-term-change on-search-term-change
|
||||
:on-search-clear-click on-search-clear-click
|
||||
:on-restore-all on-restore-all
|
||||
:show-restore-all? show-restore-all?
|
||||
:has-custom-shortcuts (some #(seq (val %)) custom-shortcuts)}]
|
||||
:show-restore-all show-restore-all
|
||||
:has-custom-shortcuts has-custom-shortcuts}]
|
||||
(if (seq filtered-shortcuts)
|
||||
[:> shortcuts-list* {:shortcuts filtered-shortcuts
|
||||
:open-sections open-sections
|
||||
@ -273,6 +297,14 @@
|
||||
(fn [new-section]
|
||||
(reset! section* (keyword new-section))))
|
||||
|
||||
open-sections-by-tab* (mf/use-state {})
|
||||
open-sections-by-tab (deref open-sections-by-tab*)
|
||||
|
||||
make-update-open-sections
|
||||
(fn [tab-key]
|
||||
(fn [new-val]
|
||||
(swap! open-sections-by-tab* assoc tab-key new-val)))
|
||||
|
||||
custom-shortcuts (get-in profile [:props :custom-shortcuts])
|
||||
has-custom-shortcuts (some #(seq (val %)) custom-shortcuts)
|
||||
path-shortcuts-custom (ds/apply-custom-overrides
|
||||
@ -322,11 +354,12 @@
|
||||
:viewer-orig vsc/shortcuts)
|
||||
|
||||
shortcuts-ctx-value
|
||||
{:workspace-sc-trans (d/deep-merge workspace-shortcuts-with-translation path-shortcuts-with-translation)
|
||||
:dashboard-sc-trans dashboard-shortcuts-with-translation
|
||||
:viewer-sc-trans viewer-shortcuts-with-translation
|
||||
:all-shortcuts all-shortcuts
|
||||
:all-sc-raw all-shortcuts-raw}
|
||||
(mf/with-memo [custom-shortcuts]
|
||||
{:workspace-sc-trans (d/deep-merge workspace-shortcuts-with-translation path-shortcuts-with-translation)
|
||||
:dashboard-sc-trans dashboard-shortcuts-with-translation
|
||||
:viewer-sc-trans viewer-shortcuts-with-translation
|
||||
:all-shortcuts all-shortcuts
|
||||
:all-sc-raw all-shortcuts-raw})
|
||||
|
||||
on-restore-all
|
||||
(mf/use-fn
|
||||
@ -336,6 +369,38 @@
|
||||
(st/emit! (modal/show {:type :restore-all-modal
|
||||
:custom-shortcuts custom-shortcuts}))))
|
||||
|
||||
filter-all
|
||||
(mf/with-memo []
|
||||
(fn [_ shortcut search-term]
|
||||
(or (str/blank? search-term)
|
||||
(matches-search (:translation shortcut) search-term))))
|
||||
|
||||
filter-personalized
|
||||
(mf/use-fn
|
||||
(mf/deps custom-shortcuts)
|
||||
(fn [shortcut-key shortcut search-term]
|
||||
(let [shortcut-group (first (:section shortcut))
|
||||
group-map (get custom-shortcuts shortcut-group)
|
||||
group-map (if (map? group-map) group-map {})
|
||||
customized? (and (contains? group-map shortcut-key)
|
||||
(not (str/blank? (get group-map shortcut-key))))]
|
||||
(and customized?
|
||||
(or (str/blank? search-term)
|
||||
(matches-search (:translation shortcut) search-term))))))
|
||||
|
||||
filter-disabled
|
||||
(mf/use-fn
|
||||
(mf/deps custom-shortcuts)
|
||||
(fn [shortcut-key shortcut search-term]
|
||||
(let [shortcut-group (first (:section shortcut))
|
||||
group-map (get custom-shortcuts shortcut-group)
|
||||
group-map (if (map? group-map) group-map {})
|
||||
in-group? (contains? group-map shortcut-key)
|
||||
blank? (str/blank? (get group-map shortcut-key))]
|
||||
(and in-group? blank?
|
||||
(or (str/blank? search-term)
|
||||
(matches-search (:translation shortcut) search-term))))))
|
||||
|
||||
on-import-file
|
||||
(mf/use-fn
|
||||
(fn [_]
|
||||
@ -357,7 +422,7 @@
|
||||
|
||||
on-file-selected
|
||||
(mf/use-fn
|
||||
(mf/deps all-shortcuts-raw)
|
||||
(mf/deps all-shortcuts-raw custom-shortcuts)
|
||||
(fn [event]
|
||||
(let [file (-> (dom/get-target event)
|
||||
(dom/get-files)
|
||||
@ -370,7 +435,10 @@
|
||||
:keywordize-keys true)
|
||||
validation (validate-imported-shortcuts shortcuts)]
|
||||
(if (:valid? validation)
|
||||
(st/emit! (ss/import-custom-shortcuts shortcuts all-shortcuts-raw))
|
||||
(st/emit! (modal/show {:type :import-shortcuts-diff-modal
|
||||
:imported-shortcuts shortcuts
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:all-shortcuts-raw all-shortcuts-raw}))
|
||||
(st/emit! (ntf/error (tr "errors.invalid-data")))))
|
||||
(catch :default _
|
||||
(st/emit! (ntf/error (tr "errors.invalid-data"))))))))
|
||||
@ -400,43 +468,33 @@
|
||||
:class (stl/css :shortcuts-switcher)}
|
||||
(case section
|
||||
:all
|
||||
[:> shortcuts-tab-section* {:shortcut-filter (fn [_ shortcut search-term]
|
||||
(or (str/blank? search-term)
|
||||
(matches-search (:translation shortcut) search-term)))
|
||||
:show-restore-all? true
|
||||
[:> shortcuts-tab-section* {:shortcut-filter filter-all
|
||||
:show-restore-all true
|
||||
:empty-str (tr "shortcuts.no-shortcuts")
|
||||
:on-restore-all on-restore-all
|
||||
:custom-shortcuts custom-shortcuts}]
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:open-sections (get open-sections-by-tab :all [[:workspace]])
|
||||
:update-open-sections (make-update-open-sections :all)}]
|
||||
|
||||
:personalized
|
||||
[:> shortcuts-tab-section* {:shortcut-filter (fn [shortcut-key shortcut search-term]
|
||||
(let [shortcut-group (first (:section shortcut))
|
||||
group-map (get custom-shortcuts shortcut-group)
|
||||
group-map (if (map? group-map) group-map {})
|
||||
customized? (and (contains? group-map shortcut-key)
|
||||
(not (str/blank? (get group-map shortcut-key))))]
|
||||
(and customized?
|
||||
(or (str/blank? search-term)
|
||||
(matches-search (:translation shortcut) search-term)))))
|
||||
:show-restore-all? true
|
||||
[:> shortcuts-tab-section* {:shortcut-filter filter-personalized
|
||||
:show-restore-all true
|
||||
:empty-str (tr "shortcuts.no-personalized")
|
||||
:on-restore-all on-restore-all
|
||||
:custom-shortcuts custom-shortcuts}]
|
||||
:expand-all-by-default true
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:open-sections (get open-sections-by-tab :personalized [[:workspace]])
|
||||
:update-open-sections (make-update-open-sections :personalized)}]
|
||||
|
||||
:disabled
|
||||
[:> shortcuts-tab-section* {:shortcut-filter (fn [shortcut-key shortcut search-term]
|
||||
(let [shortcut-group (first (:section shortcut))
|
||||
group-map (get custom-shortcuts shortcut-group)
|
||||
group-map (if (map? group-map) group-map {})
|
||||
in-group? (contains? group-map shortcut-key)
|
||||
blank? (str/blank? (get group-map shortcut-key))]
|
||||
(and in-group? blank?
|
||||
(or (str/blank? search-term)
|
||||
(matches-search (:translation shortcut) search-term)))))
|
||||
:show-restore-all? true
|
||||
[:> shortcuts-tab-section* {:shortcut-filter filter-disabled
|
||||
:show-restore-all true
|
||||
:empty-str (tr "shortcuts.no-disabled")
|
||||
:on-restore-all on-restore-all
|
||||
:custom-shortcuts custom-shortcuts}])]]
|
||||
:expand-all-by-default true
|
||||
:custom-shortcuts custom-shortcuts
|
||||
:open-sections (get open-sections-by-tab :disabled [[:workspace]])
|
||||
:update-open-sections (make-update-open-sections :disabled)}])]]
|
||||
|
||||
[:div {:class (stl/css :shortcuts-page-footer)}
|
||||
[:div {:class (stl/css :shortcuts-info)}
|
||||
|
||||
@ -14,6 +14,9 @@
|
||||
.shortcuts-page {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
||||
// box-sizing: border-box;
|
||||
// min-block-size: 100%;
|
||||
padding-block-end: px2rem(32);
|
||||
}
|
||||
|
||||
@ -194,26 +197,20 @@
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-s);
|
||||
padding-block-start: var(--sp-m);
|
||||
}
|
||||
|
||||
.shortcuts-info {
|
||||
display: flex;
|
||||
gap: var(--sp-s);
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shortcuts-page-footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: var(--sp-s);
|
||||
padding-block-start: var(--sp-m);
|
||||
margin-block-start: auto;
|
||||
padding-block: var(--sp-m) var(--sp-s);
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
background-color: var(--app-background);
|
||||
min-inline-size: px2rem(590);
|
||||
}
|
||||
|
||||
.shortcuts-info {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
display: flex;
|
||||
gap: var(--sp-m);
|
||||
align-items: center;
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
@ -229,11 +226,6 @@
|
||||
background-color: var(--color-accent-error);
|
||||
}
|
||||
|
||||
.shortcuts-info {
|
||||
display: flex;
|
||||
gap: var(--sp-m);
|
||||
}
|
||||
|
||||
.shortcuts-info-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@ -305,7 +305,7 @@
|
||||
:key unique-key} char]))
|
||||
|
||||
(mf/defc shortcuts-keys*
|
||||
[{:keys [content command is-customized has-conflict?]}]
|
||||
[{:keys [content command is-customized has-conflict? light-shortcut]}]
|
||||
(let [managed-list (if (coll? content)
|
||||
content
|
||||
(conj () content))
|
||||
@ -324,7 +324,8 @@
|
||||
:command command
|
||||
:class (cond
|
||||
has-conflict? (stl/css :conflict-key)
|
||||
is-customized (stl/css :customized-key))}])
|
||||
is-customized (stl/css :customized-key)
|
||||
light-shortcut (stl/css :light-key))}])
|
||||
|
||||
(when (not= chars penultimate) [:span {:class (stl/css :space)} ","])])
|
||||
(when (not= last-element penultimate)
|
||||
@ -336,7 +337,8 @@
|
||||
:command command
|
||||
:class (cond
|
||||
has-conflict? (stl/css :conflict-key)
|
||||
is-customized (stl/css :customized-key))}])])]))
|
||||
is-customized (stl/css :customized-key)
|
||||
light-shortcut (stl/css :light-key))}])])]))
|
||||
|
||||
(mf/defc shortcut-row-editable*
|
||||
[{:keys [elements custom-shortcuts command-translate conflicts section-key]}]
|
||||
@ -371,21 +373,30 @@
|
||||
conflict* (mf/use-state nil)
|
||||
conflict (deref conflict*)
|
||||
|
||||
reset-notification* (mf/use-state nil)
|
||||
reset-pending* (mf/use-state false)
|
||||
|
||||
clean-editing-state
|
||||
(fn []
|
||||
(reset! is-editing* false)
|
||||
(reset! display-parts* nil)
|
||||
(reset! recorded-command* nil)
|
||||
(reset! conflict* nil))
|
||||
(reset! conflict* nil)
|
||||
(reset! reset-notification* nil)
|
||||
(reset! reset-pending* false))
|
||||
|
||||
effective-section-key (if (= section-key :basics) :workspace section-key)
|
||||
|
||||
on-reset-shortcut
|
||||
(mf/use-fn
|
||||
(mf/deps effective-section-key command-info)
|
||||
(fn [shortcut-key]
|
||||
(st/emit! (customize/reset-custom-shortcut shortcut-key (:original-command command-info) effective-section-key))
|
||||
(clean-editing-state)))
|
||||
(fn [_]
|
||||
(let [original-cmd (:original-command command-info)]
|
||||
(reset! reset-pending* true)
|
||||
(reset! recorded-command* original-cmd)
|
||||
(reset! reset-notification* original-cmd)
|
||||
(reset! conflict* nil)
|
||||
(reset! display-parts* nil))))
|
||||
|
||||
start-editing
|
||||
(mf/use-fn
|
||||
@ -419,11 +430,13 @@
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(when recorded-command
|
||||
(st/emit! (customize/set-custom-shortcut
|
||||
command
|
||||
recorded-command
|
||||
(:key conflict)
|
||||
effective-section-key)))
|
||||
(if (deref reset-pending*)
|
||||
(st/emit! (customize/reset-custom-shortcut command recorded-command effective-section-key))
|
||||
(st/emit! (customize/set-custom-shortcut
|
||||
command
|
||||
recorded-command
|
||||
(:key conflict)
|
||||
effective-section-key))))
|
||||
(clean-editing-state)))
|
||||
|
||||
on-editable-container-blur
|
||||
@ -437,6 +450,11 @@
|
||||
(when is-editing
|
||||
(some-> (mf/ref-val recording-ref) (dom/focus!))))
|
||||
|
||||
(mf/with-effect [display-parts]
|
||||
(when (some? display-parts)
|
||||
(reset! reset-notification* nil)
|
||||
(reset! reset-pending* false)))
|
||||
|
||||
(mf/with-effect [is-editing]
|
||||
(when is-editing
|
||||
(letfn [(on-keydown [^js event]
|
||||
@ -506,7 +524,15 @@
|
||||
:customized-key customized?)} (:final-key display-parts)])
|
||||
(when-not (:finalized? display-parts)
|
||||
[:span {:class (stl/css :recording-ellipsis)} "..."])])]
|
||||
(when recorded-command
|
||||
(when-let [default-cmd @reset-notification*]
|
||||
[:> context-notification* {:level :info :class (stl/css :modal-reset-msg)}
|
||||
(tr "shortcuts.edit-modal.reset-pending")
|
||||
[:> shortcuts-keys* {:content default-cmd
|
||||
:command (keyword "default")
|
||||
:is-customized false
|
||||
:has-conflict? false}]])
|
||||
|
||||
(when (and recorded-command (not (deref reset-pending*)))
|
||||
(if conflict
|
||||
[:> context-notification* {:level :warning :class (stl/css :modal-error-msg)}
|
||||
(tr "shortcuts.edit-modal.conflict" (:name conflict))]
|
||||
|
||||
@ -187,6 +187,11 @@
|
||||
color: var(--color-foreground-error);
|
||||
}
|
||||
|
||||
.light-key {
|
||||
background-color: var(--color-background-tertiary);
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
.space {
|
||||
margin: 0 var(--sp-xxs);
|
||||
color: var(--color-foreground-secondary);
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
(ns frontend-tests.ui.settings-shortcuts-test
|
||||
(:require
|
||||
[app.main.data.profile :as du]
|
||||
[app.main.ui.settings.import-shortcuts-diff-modal :as diff-modal]
|
||||
[app.main.ui.settings.restore-shortcuts-modal :as restore-modal]
|
||||
[app.main.ui.settings.shortcuts :as sut]
|
||||
[app.main.ui.shortcuts :as ui-shortcuts]
|
||||
@ -150,6 +151,48 @@
|
||||
(t/is (= "ctrl+up" (get customs :move-to-top))
|
||||
"Last imported entry with duplicate command should survive")))))
|
||||
|
||||
(t/deftest compute-diff-shows-new-shortcut-changes
|
||||
(let [imported {:workspace {:add-comment "alt+c"}}
|
||||
diff (diff-modal/compute-diff imported {})]
|
||||
(t/is (= 1 (count diff)))
|
||||
(let [entry (first diff)]
|
||||
(t/is (= :workspace (:context entry)))
|
||||
(t/is (= :add-comment (:key entry)))
|
||||
(t/is (= "alt+c" (:imported entry)))
|
||||
(t/is (not= "alt+c" (:current entry))))))
|
||||
|
||||
(t/deftest compute-diff-shows-conflict-as-disabled
|
||||
(let [imported {:workspace {:select-all "ctrl+shift+a"}}
|
||||
diff (diff-modal/compute-diff imported {})]
|
||||
(t/is (pos? (count diff)))
|
||||
(let [select-all-entry (first (filter #(= :select-all (:key %)) diff))]
|
||||
(t/is (= :workspace (:context select-all-entry)))
|
||||
(t/is (= "ctrl+shift+a" (:imported select-all-entry))))))
|
||||
|
||||
(t/deftest compute-diff-shows-empty-string-as-disabled-shortcut
|
||||
(let [imported {:workspace {:escape ""}}
|
||||
current {}
|
||||
diff (diff-modal/compute-diff imported current)]
|
||||
(t/is (pos? (count diff)))
|
||||
(let [entry (first (filter #(= :escape (:key %)) diff))]
|
||||
(t/is (= :workspace (:context entry)))
|
||||
(t/is (= "" (:imported entry))))))
|
||||
|
||||
(t/deftest compute-diff-excludes-unchanged-shortcuts
|
||||
(let [imported {:workspace {:escape ""}}
|
||||
current {:workspace {:escape ""}}
|
||||
diff (diff-modal/compute-diff imported current)]
|
||||
(t/is (empty? diff))))
|
||||
|
||||
(t/deftest compute-diff-detects-multiple-contexts
|
||||
(let [imported {:workspace {:add-comment "alt+c"}
|
||||
:viewer {:next-frame "right"}}
|
||||
diff (diff-modal/compute-diff imported {})]
|
||||
(t/is (pos? (count diff)))
|
||||
(let [contexts (set (map :context diff))]
|
||||
(t/is (contains? contexts :workspace))
|
||||
(t/is (contains? contexts :viewer)))))
|
||||
|
||||
(t/deftest extract-shortcut-keys-uses-correct-context-override
|
||||
(let [customs {:workspace {:undo "shift+z"
|
||||
:move-nodes "shift+m"}
|
||||
|
||||
@ -5631,12 +5631,8 @@ msgid "shortcuts.start-editing"
|
||||
msgstr "Start editing"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:182
|
||||
msgid "shortcuts.start-measure"
|
||||
msgstr "Start measurement"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:183
|
||||
msgid "shortcuts.stop-measure"
|
||||
msgstr "Stop measurement"
|
||||
msgid "shortcuts.show-measure"
|
||||
msgstr "Show measurements"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:184
|
||||
msgid "shortcuts.thumbnail-set"
|
||||
@ -5710,6 +5706,10 @@ msgstr "Press the key combination"
|
||||
msgid "shortcuts.edit-modal.conflict"
|
||||
msgstr "Combination assigned to \"%s\". Saving will remove it there."
|
||||
|
||||
#: src/app/main/ui/shortcuts.cljs
|
||||
msgid "shortcuts.edit-modal.reset-pending"
|
||||
msgstr "If you save, this shortcut will return to the original value:"
|
||||
|
||||
#: src/app/main/ui/shortcuts.cljs
|
||||
msgid "shortcuts.edit-modal.success"
|
||||
msgstr "This key combination is available and can be assigned without conflicts."
|
||||
@ -5846,6 +5846,22 @@ msgstr "Acction"
|
||||
msgid "restore-shortcuts.restore"
|
||||
msgstr "Restore"
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.diff-modal-title"
|
||||
msgstr "Import shortcuts"
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.diff-modal-text"
|
||||
msgstr "The following shortcuts will be changed by the imported configuration."
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.no-changes"
|
||||
msgstr "No differences found between the current and imported shortcuts."
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.apply"
|
||||
msgstr "Apply"
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:191
|
||||
msgid "subscription.banner.create-org-info"
|
||||
msgstr ""
|
||||
|
||||
@ -5486,12 +5486,8 @@ msgid "shortcuts.start-editing"
|
||||
msgstr "Comenzar edición"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:182
|
||||
msgid "shortcuts.start-measure"
|
||||
msgstr "Comenzar medida"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:183
|
||||
msgid "shortcuts.stop-measure"
|
||||
msgstr "Terminar medida"
|
||||
msgid "shortcuts.show-measure"
|
||||
msgstr "Mostrar medidas"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/shortcuts.cljs:184
|
||||
msgid "shortcuts.thumbnail-set"
|
||||
@ -5565,6 +5561,10 @@ msgstr "Pulsa la combinación de teclas deseada"
|
||||
msgid "shortcuts.edit-modal.conflict"
|
||||
msgstr "Combinación usada en \"%s\". Guardar este atajo hará que se elimine allí."
|
||||
|
||||
#: src/app/main/ui/shortcuts.cljs
|
||||
msgid "shortcuts.edit-modal.reset-pending"
|
||||
msgstr "Si guardas, este atajo volvera a su valor original:"
|
||||
|
||||
#: src/app/main/ui/shortcuts.cljs
|
||||
msgid "shortcuts.edit-modal.success"
|
||||
msgstr "Esta combinación de teclas está disponible y puede asignarse sin conflictos."
|
||||
@ -5701,6 +5701,22 @@ msgstr "Acción"
|
||||
msgid "restore-shortcuts.restore"
|
||||
msgstr "Restaurar"
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.diff-modal-title"
|
||||
msgstr "Importar atajos"
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.diff-modal-text"
|
||||
msgstr "Los siguientes atajos se modificarán con la configuración importada."
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.no-changes"
|
||||
msgstr "No se encontraron diferencias entre los atajos actuales y los importados."
|
||||
|
||||
#: src/app/main/ui/settings/import_shortcuts_diff_modal.cljs
|
||||
msgid "import-shortcuts.apply"
|
||||
msgstr "Aplicar"
|
||||
|
||||
#: src/app/main/ui/dashboard/subscription.cljs:191
|
||||
msgid "subscription.banner.create-org-info"
|
||||
msgstr ""
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user