0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-05 03:21:26 -05:00

New menu entry for change theme

This commit is contained in:
alonso.torres 2024-01-31 10:10:18 +01:00
parent 14584ef920
commit 1f2f70fcd4
7 changed files with 889 additions and 810 deletions

View file

@ -351,21 +351,32 @@
[from-p move-p command-pts]
(case (:command command)
:move-to [to-p to-p [to-p]]
:close-path [move-p move-p [move-p]]
:line-to [to-p move-p [from-p to-p]]
:move-to [to-p to-p (when to-p [to-p])]
:close-path [move-p move-p (when move-p [move-p])]
:line-to [to-p move-p (when (and from-p to-p) [from-p to-p])]
:curve-to [to-p move-p
(let [c1 (command->point command :c1)
c2 (command->point command :c2)
curve [from-p to-p c1 c2]]
(into [from-p to-p]
(->> (curve-extremities curve)
(map #(curve-values curve %)))))]
(when (and from-p to-p c1 c2)
(into [from-p to-p]
(->> (curve-extremities curve)
(map #(curve-values curve %))))))]
[to-p move-p []])]
(recur (apply conj points command-pts) from-p move-p (next content)))
points))]
(grc/points->rect extremities)))
points))
;; We haven't found any extremes so we turn the commands to points
extremities
(if (empty? extremities)
(->> content (keep command->point))
extremities)]
;; If no points are returned we return an empty rect.
(if (d/not-empty? extremities)
(grc/points->rect extremities)
(grc/make-rect))))
(defn move-content [content move-vec]
(let [dx (:x move-vec)

View file

@ -8,727 +8,28 @@
(:require-macros [app.main.style :as stl])
(:require
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.main.data.common :as dcm]
[app.main.data.events :as ev]
[app.main.data.exports :as de]
[app.main.data.modal :as modal]
[app.main.data.shortcuts :as scd]
[app.main.data.workspace :as dw]
[app.main.data.workspace.colors :as dc]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.shortcuts :as sc]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
[app.main.ui.context :as ctx]
[app.main.ui.hooks.resize :as r]
[app.main.ui.icons :as i]
[app.main.ui.workspace.main-menu :as main-menu]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[app.util.router :as rt]
[cuerdas.core :as str]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
;; --- Header menu and submenus
(mf/defc help-info-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [layout on-close]}]
(let [nav-to-helpc-center
(mf/use-fn #(dom/open-new-window "https://help.penpot.app"))
nav-to-community
(mf/use-fn #(dom/open-new-window "https://community.penpot.app"))
nav-to-youtube
(mf/use-fn #(dom/open-new-window "https://www.youtube.com/c/Penpot"))
nav-to-templates
(mf/use-fn #(dom/open-new-window "https://penpot.app/libraries-templates"))
nav-to-github
(mf/use-fn #(dom/open-new-window "https://github.com/penpot/penpot"))
nav-to-terms
(mf/use-fn #(dom/open-new-window "https://penpot.app/terms"))
nav-to-feedback
(mf/use-fn #(st/emit! (rt/nav-new-window* {:rname :settings-feedback})))
show-shortcuts
(mf/use-fn
(mf/deps layout)
(fn []
(when (contains? layout :collapse-left-sidebar)
(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))
(st/emit!
(-> (dw/toggle-layout-flag :shortcuts)
(vary-meta assoc ::ev/origin "workspace-header")))))
show-release-notes
(mf/use-fn
(fn [event]
(let [version (:main cf/version)]
(st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version version}))
(if (and (kbd/alt? event) (kbd/mod? event))
(st/emit! (modal/show {:type :onboarding}))
(st/emit! (modal/show {:type :release-notes :version version}))))))]
[:& dropdown-menu {:show true
:on-close on-close
:list-class (stl/css-case :sub-menu true
:help-info true)}
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-helpc-center
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-helpc-center event)))
:id "file-menu-help-center"}
[:span {:class (stl/css :item-name)} (tr "labels.help-center")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-community
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-community event)))
:id "file-menu-community"}
[:span {:class (stl/css :item-name)} (tr "labels.community")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-youtube
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-youtube event)))
:id "file-menu-youtube"}
[:span {:class (stl/css :item-name)} (tr "labels.tutorials")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click show-release-notes
:on-key-down (fn [event]
(when (kbd/enter? event)
(show-release-notes event)))
:id "file-menu-release-notes"}
[:span {:class (stl/css :item-name)} (tr "labels.release-notes")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-templates
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-templates event)))
:id "file-menu-templates"}
[:span {:class (stl/css :item-name)} (tr "labels.libraries-and-templates")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-github
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-github event)))
:id "file-menu-github"}
[:span {:class (stl/css :item-name)} (tr "labels.github-repo")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-terms
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-terms event)))
:id "file-menu-terms"}
[:span {:class (stl/css :item-name)} (tr "auth.terms-of-service")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click show-shortcuts
:on-key-down (fn [event]
(when (kbd/enter? event)
(show-shortcuts event)))
:id "file-menu-shortcuts"}
[:span {:class (stl/css :item-name)} (tr "label.shortcuts")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :show-shortcuts))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
(when (contains? cf/flags :user-feedback)
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-feedback
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-feedback event)))
:id "file-menu-feedback"}
[:span {:class (stl/css-case :feedback true
:item-name true)} (tr "labels.give-feedback")]])]))
(mf/defc preferences-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [layout toggle-flag on-close]}]
(let [show-nudge-options (mf/use-fn #(modal/show! {:type :nudge-option}))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:preferences true)
:on-close on-close}
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "scale-text"
:id "file-menu-scale-text"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :scale-text)
(tr "workspace.header.menu.disable-scale-content")
(tr "workspace.header.menu.enable-scale-content"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-scale-text))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "snap-guides"
:id "file-menu-snap-guides"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :snap-guides)
(tr "workspace.header.menu.disable-snap-guides")
(tr "workspace.header.menu.enable-snap-guides"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-snap-guide))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "snap-grid"
:id "file-menu-snap-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :snap-grid)
(tr "workspace.header.menu.disable-snap-grid")
(tr "workspace.header.menu.enable-snap-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-snap-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "dynamic-alignment"
:id "file-menu-dynamic-alignment"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :dynamic-alignment)
(tr "workspace.header.menu.disable-dynamic-alignment")
(tr "workspace.header.menu.enable-dynamic-alignment"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-alignment))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "snap-pixel-grid"
:id "file-menu-pixel-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :snap-pixel-grid)
(tr "workspace.header.menu.disable-snap-pixel-grid")
(tr "workspace.header.menu.enable-snap-pixel-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :snap-pixel-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click show-nudge-options
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(show-nudge-options event)))
:data-test "snap-pixel-grid"
:id "file-menu-nudge"}
[:span {:class (stl/css :item-name)} (tr "modals.nudge-title")]]]))
(mf/defc view-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [layout toggle-flag on-close]}]
(let [read-only? (mf/use-ctx ctx/workspace-read-only?)
toggle-color-palette
(mf/use-fn
(fn []
(r/set-resize-type! :bottom)
(st/emit! (dw/remove-layout-flag :textpalette)
(-> (dw/toggle-layout-flag :colorpalette)
(vary-meta assoc ::ev/origin "workspace-menu")))))
toggle-text-palette
(mf/use-fn
(fn []
(r/set-resize-type! :bottom)
(st/emit! (dw/remove-layout-flag :colorpalette)
(-> (dw/toggle-layout-flag :textpalette)
(vary-meta assoc ::ev/origin "workspace-menu")))))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:view true)
:on-close on-close}
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "rules"
:id "file-menu-rules"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :rules)
(tr "workspace.header.menu.hide-rules")
(tr "workspace.header.menu.show-rules"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-rules))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "display-grid"
:id "file-menu-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :display-grid)
(tr "workspace.header.menu.hide-grid")
(tr "workspace.header.menu.show-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
(when-not ^boolean read-only?
[:*
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-color-palette
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-color-palette event)))
:id "file-menu-color-palette"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :colorpalette)
(tr "workspace.header.menu.hide-palette")
(tr "workspace.header.menu.show-palette"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-colorpalette))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-text-palette
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-text-palette event)))
:id "file-menu-text-palette"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :textpalette)
(tr "workspace.header.menu.hide-textpalette")
(tr "workspace.header.menu.show-textpalette"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-textpalette))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]])
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "display-artboard-names"
:id "file-menu-artboards"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :display-artboard-names)
(tr "workspace.header.menu.hide-artboard-names")
(tr "workspace.header.menu.show-artboard-names"))]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "show-pixel-grid"
:id "file-menu-pixel-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :show-pixel-grid)
(tr "workspace.header.menu.hide-pixel-grid")
(tr "workspace.header.menu.show-pixel-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :show-pixel-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "hide-ui"
:id "file-menu-hide-ui"}
[:span {:class (stl/css :item-name)}
(tr "workspace.shape.menu.hide-ui")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :hide-ui))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))
(mf/defc edit-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [on-close]}]
(let [select-all (mf/use-fn #(st/emit! (dw/select-all)))
undo (mf/use-fn #(st/emit! dwc/undo))
redo (mf/use-fn #(st/emit! dwc/redo))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:edit true)
:on-close on-close}
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click select-all
:on-key-down (fn [event]
(when (kbd/enter? event)
(select-all event)))
:id "file-menu-select-all"}
[:span {:class (stl/css :item-name)}
(tr "workspace.header.menu.select-all")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :select-all))]
[:span {:class (stl/css :shortcut-key)
:key sc}
sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click undo
:on-key-down (fn [event]
(when (kbd/enter? event)
(undo event)))
:id "file-menu-undo"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.undo")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :undo))]
[:span {:class (stl/css :shortcut-key)
:key sc}
sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click redo
:on-key-down (fn [event]
(when (kbd/enter? event)
(redo event)))
:id "file-menu-redo"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.redo")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :redo))]
[:span {:class (stl/css :shortcut-key)
:key sc}
sc])]]]))
(mf/defc file-menu
{::mf/wrap-props false}
[{:keys [on-close file]}]
(let [file-id (:id file)
shared? (:is-shared file)
objects (mf/deref refs/workspace-page-objects)
frames (->> (cfh/get-immediate-children objects uuid/zero)
(filterv cfh/frame-shape?))
on-remove-shared
(mf/use-fn
(mf/deps file-id)
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(modal/show!
{:type :delete-shared-libraries
:origin :unpublish
:ids #{file-id}
:on-accept #(st/emit! (dwl/set-file-shared file-id false))
:count-libraries 1})))
on-remove-shared-key-down
(mf/use-fn
(mf/deps on-remove-shared)
(fn [event]
(when (kbd/enter? event)
(on-remove-shared event))))
on-add-shared
(mf/use-fn
(mf/deps file-id)
(fn [_event]
(let [on-accept #(st/emit! (dwl/set-file-shared file-id true))]
(st/emit! (dcm/show-shared-dialog file-id on-accept)))))
on-add-shared-key-down
(mf/use-fn
(mf/deps on-add-shared)
(fn [event]
(when (kbd/enter? event)
(on-add-shared event))))
on-export-shapes
(mf/use-fn #(st/emit! (de/show-workspace-export-dialog)))
on-export-shapes-key-down
(mf/use-fn
(mf/deps on-export-shapes)
(fn [event]
(when (kbd/enter? event)
(on-export-shapes event))))
on-export-file
(mf/use-fn
(mf/deps file)
(fn [event]
(let [target (dom/get-current-target event)
binary? (= (dom/get-data target "binary") "true")
evname (if binary?
"export-binary-files"
"export-standard-files")]
(st/emit!
(ptk/event ::ev/event {::ev/name evname
::ev/origin "workspace"
:num-files 1})
(dcm/export-files [file] binary?)))))
on-export-file-key-down
(mf/use-fn
(mf/deps on-export-file)
(fn [event]
(when (kbd/enter? event)
(on-export-file event))))
on-export-frames
(mf/use-fn
(mf/deps frames)
(fn [_]
(st/emit! (de/show-workspace-export-frames-dialog (reverse frames)))))
on-export-frames-key-down
(mf/use-fn
(mf/deps on-export-frames)
(fn [event]
(when (kbd/enter? event)
(on-export-frames event))))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:file true)
:on-close on-close}
(if ^boolean shared?
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-remove-shared
:on-key-down on-remove-shared-key-down
:id "file-menu-remove-shared"}
[:span {:class (stl/css :item-name)} (tr "dashboard.unpublish-shared")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-add-shared
:on-key-down on-add-shared-key-down
:id "file-menu-add-shared"}
[:span {:class (stl/css :item-name)} (tr "dashboard.add-shared")]])
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-shapes
:on-key-down on-export-shapes-key-down
:id "file-menu-export-shapes"}
[:span {:class (stl/css :item-name)} (tr "dashboard.export-shapes")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :export-shapes))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-file
:on-key-down on-export-file-key-down
:data-binary true
:id "file-menu-binary-file"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.download-binary-file")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-file
:on-key-down on-export-file-key-down
:data-binary false
:id "file-menu-standard-file"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.download-standard-file")]]
(when (seq frames)
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-frames
:on-key-down on-export-frames-key-down
:id "file-menu-export-frames"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.export-frames")]])]))
(mf/defc menu
{::mf/wrap-props false}
[{:keys [layout file]}]
(let [show-menu* (mf/use-state false)
show-menu? (deref show-menu*)
sub-menu* (mf/use-state false)
sub-menu (deref sub-menu*)
open-menu
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! show-menu* true)))
close-menu
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! show-menu* false)))
close-sub-menu
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! sub-menu* nil)))
on-menu-click
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(let [menu (-> (dom/get-current-target event)
(dom/get-data "test")
(keyword))]
(reset! sub-menu* menu))))
toggle-flag
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(let [flag (-> (dom/get-current-target event)
(dom/get-data "test")
(keyword))]
(st/emit!
(-> (dw/toggle-layout-flag flag)
(vary-meta assoc ::ev/origin "workspace-menu")))
(reset! show-menu* false)
(reset! sub-menu* nil))))]
[:*
[:div {:on-click open-menu
:class (stl/css :menu-btn)} i/menu-refactor]
[:& dropdown-menu {:show show-menu?
:on-close close-menu
:list-class (stl/css :menu)}
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "file"
:id "file-menu-file"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.file")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "edit"
:id "file-menu-edit"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.edit")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "view"
:id "file-menu-view"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.view")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "preferences"
:id "file-menu-preferences"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.preferences")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:div {:class (stl/css :separator)}]
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "help-info"
:id "file-menu-help-info"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.help-info")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]]
(case sub-menu
:file
[:& file-menu
{:file file
:on-close close-sub-menu}]
:edit
[:& edit-menu
{:on-close close-sub-menu}]
:view
[:& view-menu
{:layout layout
:toggle-flag toggle-flag
:on-close close-sub-menu}]
:preferences
[:& preferences-menu
{:layout layout
:toggle-flag toggle-flag
:on-close close-sub-menu}]
:help-info
[:& help-info-menu
{:layout layout
:on-close close-sub-menu}]
nil)]))
;; --- Header Component
(mf/defc left-header
{::mf/wrap-props false}
[{:keys [file layout project page-id class]}]
(let [file-id (:id file)
(let [profile (mf/deref refs/profile)
file-id (:id file)
file-name (:name file)
project-id (:id project)
team-id (:team-id project)
@ -809,9 +110,10 @@
(when ^boolean shared?
[:span {:class (stl/css :shared-badge)} i/library-refactor])
[:div {:class (stl/css :menu-section)}
[:& menu {:layout layout
:file file
:read-only? read-only?
:team-id team-id
:page-id page-id}]]]))
[:& main-menu/menu
{:layout layout
:file file
:profile profile
:read-only? read-only?
:team-id team-id
:page-id page-id}]]]))

View file

@ -77,97 +77,3 @@
width: $s-16;
}
}
.menu-btn {
@extend .button-tertiary;
height: $s-32;
width: calc($s-24 + $s-4);
padding: 0;
border-radius: $br-8;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
.menu {
@extend .menu-dropdown;
top: $s-48;
left: calc(var(--width, $s-256) - $s-16);
width: $s-192;
margin: 0;
}
.menu-item {
@extend .menu-item-base;
cursor: pointer;
.open-arrow {
@include flexCenter;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
&:hover {
color: var(--menu-foreground-color-hover);
.open-arrow {
svg {
stroke: var(--menu-foreground-color-hover);
}
}
.shortcut-key {
color: var(--menu-shortcut-foreground-color-hover);
}
}
}
.separator {
margin-top: $s-8;
height: $s-4;
border-top: $s-1 solid $db-secondary;
}
.shortcut {
@extend .shortcut-base;
}
.shortcut-key {
@extend .shortcut-key-base;
}
.sub-menu {
@extend .menu-dropdown;
left: calc(var(--width, $s-256) + $s-180);
width: $s-192;
min-width: calc($s-272 - $s-2);
width: 110%;
.submenu-item {
@extend .menu-item-base;
&:hover {
color: var(--menu-foreground-color-hover);
.shortcut-key {
color: var(--menu-shortcut-foreground-color-hover);
}
}
}
&.file {
top: $s-48;
}
&.edit {
top: $s-76;
}
&.view {
top: $s-116;
}
&.preferences {
top: $s-148;
}
&.help-info {
top: $s-196;
}
}

View file

@ -0,0 +1,747 @@
;; 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
(ns app.main.ui.workspace.main-menu
(:require-macros [app.main.style :as stl])
(:require
[app.common.files.helpers :as cfh]
[app.common.uuid :as uuid]
[app.config :as cf]
[app.main.data.common :as dcm]
[app.main.data.events :as ev]
[app.main.data.exports :as de]
[app.main.data.modal :as modal]
[app.main.data.shortcuts :as scd]
[app.main.data.users :as du]
[app.main.data.workspace :as dw]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.shortcuts :as sc]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown-menu :refer [dropdown-menu dropdown-menu-item*]]
[app.main.ui.context :as ctx]
[app.main.ui.hooks.resize :as r]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[app.util.router :as rt]
[potok.v2.core :as ptk]
[rumext.v2 :as mf]))
;; --- Header menu and submenus
(mf/defc help-info-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [layout on-close]}]
(let [nav-to-helpc-center
(mf/use-fn #(dom/open-new-window "https://help.penpot.app"))
nav-to-community
(mf/use-fn #(dom/open-new-window "https://community.penpot.app"))
nav-to-youtube
(mf/use-fn #(dom/open-new-window "https://www.youtube.com/c/Penpot"))
nav-to-templates
(mf/use-fn #(dom/open-new-window "https://penpot.app/libraries-templates"))
nav-to-github
(mf/use-fn #(dom/open-new-window "https://github.com/penpot/penpot"))
nav-to-terms
(mf/use-fn #(dom/open-new-window "https://penpot.app/terms"))
nav-to-feedback
(mf/use-fn #(st/emit! (rt/nav-new-window* {:rname :settings-feedback})))
show-shortcuts
(mf/use-fn
(mf/deps layout)
(fn []
(when (contains? layout :collapse-left-sidebar)
(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))
(st/emit!
(-> (dw/toggle-layout-flag :shortcuts)
(vary-meta assoc ::ev/origin "workspace-header")))))
show-release-notes
(mf/use-fn
(fn [event]
(let [version (:main cf/version)]
(st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version version}))
(if (and (kbd/alt? event) (kbd/mod? event))
(st/emit! (modal/show {:type :onboarding}))
(st/emit! (modal/show {:type :release-notes :version version}))))))]
[:& dropdown-menu {:show true
:on-close on-close
:list-class (stl/css-case :sub-menu true
:help-info true)}
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-helpc-center
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-helpc-center event)))
:id "file-menu-help-center"}
[:span {:class (stl/css :item-name)} (tr "labels.help-center")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-community
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-community event)))
:id "file-menu-community"}
[:span {:class (stl/css :item-name)} (tr "labels.community")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-youtube
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-youtube event)))
:id "file-menu-youtube"}
[:span {:class (stl/css :item-name)} (tr "labels.tutorials")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click show-release-notes
:on-key-down (fn [event]
(when (kbd/enter? event)
(show-release-notes event)))
:id "file-menu-release-notes"}
[:span {:class (stl/css :item-name)} (tr "labels.release-notes")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-templates
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-templates event)))
:id "file-menu-templates"}
[:span {:class (stl/css :item-name)} (tr "labels.libraries-and-templates")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-github
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-github event)))
:id "file-menu-github"}
[:span {:class (stl/css :item-name)} (tr "labels.github-repo")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-terms
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-terms event)))
:id "file-menu-terms"}
[:span {:class (stl/css :item-name)} (tr "auth.terms-of-service")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click show-shortcuts
:on-key-down (fn [event]
(when (kbd/enter? event)
(show-shortcuts event)))
:id "file-menu-shortcuts"}
[:span {:class (stl/css :item-name)} (tr "label.shortcuts")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :show-shortcuts))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
(when (contains? cf/flags :user-feedback)
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click nav-to-feedback
:on-key-down (fn [event]
(when (kbd/enter? event)
(nav-to-feedback event)))
:id "file-menu-feedback"}
[:span {:class (stl/css-case :feedback true
:item-name true)} (tr "labels.give-feedback")]])]))
(mf/defc preferences-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [layout profile toggle-flag on-close toggle-theme]}]
(let [show-nudge-options (mf/use-fn #(modal/show! {:type :nudge-option}))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:preferences true)
:on-close on-close}
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "scale-text"
:id "file-menu-scale-text"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :scale-text)
(tr "workspace.header.menu.disable-scale-content")
(tr "workspace.header.menu.enable-scale-content"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-scale-text))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "snap-guides"
:id "file-menu-snap-guides"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :snap-guides)
(tr "workspace.header.menu.disable-snap-guides")
(tr "workspace.header.menu.enable-snap-guides"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-snap-guide))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "snap-grid"
:id "file-menu-snap-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :snap-grid)
(tr "workspace.header.menu.disable-snap-grid")
(tr "workspace.header.menu.enable-snap-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-snap-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "dynamic-alignment"
:id "file-menu-dynamic-alignment"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :dynamic-alignment)
(tr "workspace.header.menu.disable-dynamic-alignment")
(tr "workspace.header.menu.enable-dynamic-alignment"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-alignment))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click toggle-flag
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "snap-pixel-grid"
:id "file-menu-pixel-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :snap-pixel-grid)
(tr "workspace.header.menu.disable-snap-pixel-grid")
(tr "workspace.header.menu.enable-snap-pixel-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :snap-pixel-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:on-click show-nudge-options
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(show-nudge-options event)))
:data-test "snap-pixel-grid"
:id "file-menu-nudge"}
[:span {:class (stl/css :item-name)} (tr "modals.nudge-title")]]
[:> dropdown-menu-item* {:on-click toggle-theme
:class (stl/css :submenu-item)
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-theme event)))
:data-test "toggle-theme"
:id "file-menu-toggle-theme"}
[:span {:class (stl/css :item-name)}
(if (= (:theme profile) "default")
(tr "workspace.header.menu.toggle-light-theme")
(tr "workspace.header.menu.toggle-dark-theme"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-theme))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))
(mf/defc view-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [layout toggle-flag on-close]}]
(let [read-only? (mf/use-ctx ctx/workspace-read-only?)
toggle-color-palette
(mf/use-fn
(fn []
(r/set-resize-type! :bottom)
(st/emit! (dw/remove-layout-flag :textpalette)
(-> (dw/toggle-layout-flag :colorpalette)
(vary-meta assoc ::ev/origin "workspace-menu")))))
toggle-text-palette
(mf/use-fn
(fn []
(r/set-resize-type! :bottom)
(st/emit! (dw/remove-layout-flag :colorpalette)
(-> (dw/toggle-layout-flag :textpalette)
(vary-meta assoc ::ev/origin "workspace-menu")))))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:view true)
:on-close on-close}
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "rules"
:id "file-menu-rules"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :rules)
(tr "workspace.header.menu.hide-rules")
(tr "workspace.header.menu.show-rules"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-rules))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "display-grid"
:id "file-menu-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :display-grid)
(tr "workspace.header.menu.hide-grid")
(tr "workspace.header.menu.show-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
(when-not ^boolean read-only?
[:*
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-color-palette
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-color-palette event)))
:id "file-menu-color-palette"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :colorpalette)
(tr "workspace.header.menu.hide-palette")
(tr "workspace.header.menu.show-palette"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-colorpalette))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-text-palette
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-text-palette event)))
:id "file-menu-text-palette"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :textpalette)
(tr "workspace.header.menu.hide-textpalette")
(tr "workspace.header.menu.show-textpalette"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :toggle-textpalette))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]])
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "display-artboard-names"
:id "file-menu-artboards"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :display-artboard-names)
(tr "workspace.header.menu.hide-artboard-names")
(tr "workspace.header.menu.show-artboard-names"))]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "show-pixel-grid"
:id "file-menu-pixel-grid"}
[:span {:class (stl/css :item-name)}
(if (contains? layout :show-pixel-grid)
(tr "workspace.header.menu.hide-pixel-grid")
(tr "workspace.header.menu.show-pixel-grid"))]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :show-pixel-grid))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click toggle-flag
:on-key-down (fn [event]
(when (kbd/enter? event)
(toggle-flag event)))
:data-test "hide-ui"
:id "file-menu-hide-ui"}
[:span {:class (stl/css :item-name)}
(tr "workspace.shape.menu.hide-ui")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :hide-ui))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]]))
(mf/defc edit-menu
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[{:keys [on-close]}]
(let [select-all (mf/use-fn #(st/emit! (dw/select-all)))
undo (mf/use-fn #(st/emit! dwc/undo))
redo (mf/use-fn #(st/emit! dwc/redo))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:edit true)
:on-close on-close}
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click select-all
:on-key-down (fn [event]
(when (kbd/enter? event)
(select-all event)))
:id "file-menu-select-all"}
[:span {:class (stl/css :item-name)}
(tr "workspace.header.menu.select-all")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :select-all))]
[:span {:class (stl/css :shortcut-key)
:key sc}
sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click undo
:on-key-down (fn [event]
(when (kbd/enter? event)
(undo event)))
:id "file-menu-undo"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.undo")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :undo))]
[:span {:class (stl/css :shortcut-key)
:key sc}
sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click redo
:on-key-down (fn [event]
(when (kbd/enter? event)
(redo event)))
:id "file-menu-redo"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.redo")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :redo))]
[:span {:class (stl/css :shortcut-key)
:key sc}
sc])]]]))
(mf/defc file-menu
{::mf/wrap-props false}
[{:keys [on-close file]}]
(let [file-id (:id file)
shared? (:is-shared file)
objects (mf/deref refs/workspace-page-objects)
frames (->> (cfh/get-immediate-children objects uuid/zero)
(filterv cfh/frame-shape?))
on-remove-shared
(mf/use-fn
(mf/deps file-id)
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(modal/show!
{:type :delete-shared-libraries
:origin :unpublish
:ids #{file-id}
:on-accept #(st/emit! (dwl/set-file-shared file-id false))
:count-libraries 1})))
on-remove-shared-key-down
(mf/use-fn
(mf/deps on-remove-shared)
(fn [event]
(when (kbd/enter? event)
(on-remove-shared event))))
on-add-shared
(mf/use-fn
(mf/deps file-id)
(fn [_event]
(let [on-accept #(st/emit! (dwl/set-file-shared file-id true))]
(st/emit! (dcm/show-shared-dialog file-id on-accept)))))
on-add-shared-key-down
(mf/use-fn
(mf/deps on-add-shared)
(fn [event]
(when (kbd/enter? event)
(on-add-shared event))))
on-export-shapes
(mf/use-fn #(st/emit! (de/show-workspace-export-dialog)))
on-export-shapes-key-down
(mf/use-fn
(mf/deps on-export-shapes)
(fn [event]
(when (kbd/enter? event)
(on-export-shapes event))))
on-export-file
(mf/use-fn
(mf/deps file)
(fn [event]
(let [target (dom/get-current-target event)
binary? (= (dom/get-data target "binary") "true")
evname (if binary?
"export-binary-files"
"export-standard-files")]
(st/emit!
(ptk/event ::ev/event {::ev/name evname
::ev/origin "workspace"
:num-files 1})
(dcm/export-files [file] binary?)))))
on-export-file-key-down
(mf/use-fn
(mf/deps on-export-file)
(fn [event]
(when (kbd/enter? event)
(on-export-file event))))
on-export-frames
(mf/use-fn
(mf/deps frames)
(fn [_]
(st/emit! (de/show-workspace-export-frames-dialog (reverse frames)))))
on-export-frames-key-down
(mf/use-fn
(mf/deps on-export-frames)
(fn [event]
(when (kbd/enter? event)
(on-export-frames event))))]
[:& dropdown-menu {:show true
:list-class (stl/css-case :sub-menu true
:file true)
:on-close on-close}
(if ^boolean shared?
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-remove-shared
:on-key-down on-remove-shared-key-down
:id "file-menu-remove-shared"}
[:span {:class (stl/css :item-name)} (tr "dashboard.unpublish-shared")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-add-shared
:on-key-down on-add-shared-key-down
:id "file-menu-add-shared"}
[:span {:class (stl/css :item-name)} (tr "dashboard.add-shared")]])
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-shapes
:on-key-down on-export-shapes-key-down
:id "file-menu-export-shapes"}
[:span {:class (stl/css :item-name)} (tr "dashboard.export-shapes")]
[:span {:class (stl/css :shortcut)}
(for [sc (scd/split-sc (sc/get-tooltip :export-shapes))]
[:span {:class (stl/css :shortcut-key) :key sc} sc])]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-file
:on-key-down on-export-file-key-down
:data-binary true
:id "file-menu-binary-file"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.download-binary-file")]]
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-file
:on-key-down on-export-file-key-down
:data-binary false
:id "file-menu-standard-file"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.download-standard-file")]]
(when (seq frames)
[:> dropdown-menu-item* {:class (stl/css :submenu-item)
:on-click on-export-frames
:on-key-down on-export-frames-key-down
:id "file-menu-export-frames"}
[:span {:class (stl/css :item-name)}
(tr "dashboard.export-frames")]])]))
(mf/defc menu
{::mf/wrap-props false}
[{:keys [layout file profile]}]
(let [show-menu* (mf/use-state false)
show-menu? (deref show-menu*)
sub-menu* (mf/use-state false)
sub-menu (deref sub-menu*)
open-menu
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! show-menu* true)))
close-menu
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! show-menu* false)))
close-sub-menu
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! sub-menu* nil)))
on-menu-click
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(let [menu (-> (dom/get-current-target event)
(dom/get-data "test")
(keyword))]
(reset! sub-menu* menu))))
toggle-flag
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(let [flag (-> (dom/get-current-target event)
(dom/get-data "test")
(keyword))]
(st/emit!
(-> (dw/toggle-layout-flag flag)
(vary-meta assoc ::ev/origin "workspace-menu")))
(reset! show-menu* false)
(reset! sub-menu* nil))))
toggle-theme
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(st/emit! (du/toggle-theme))))]
[:*
[:div {:on-click open-menu
:class (stl/css :menu-btn)} i/menu-refactor]
[:& dropdown-menu {:show show-menu?
:on-close close-menu
:list-class (stl/css :menu)}
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "file"
:id "file-menu-file"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.file")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "edit"
:id "file-menu-edit"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.edit")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "view"
:id "file-menu-view"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.view")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:> dropdown-menu-item* {:class (stl/css :menu-item)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "preferences"
:id "file-menu-preferences"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.preferences")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]
[:div {:class (stl/css :separator)}]
[:> dropdown-menu-item* {:class (stl/css-case :menu-item true)
:on-click on-menu-click
:on-key-down (fn [event]
(when (kbd/enter? event)
(on-menu-click event)))
:on-pointer-enter on-menu-click
:data-test "help-info"
:id "file-menu-help-info"}
[:span {:class (stl/css :item-name)} (tr "workspace.header.menu.option.help-info")]
[:span {:class (stl/css :open-arrow)} i/arrow-refactor]]]
(case sub-menu
:file
[:& file-menu
{:file file
:on-close close-sub-menu}]
:edit
[:& edit-menu
{:on-close close-sub-menu}]
:view
[:& view-menu
{:layout layout
:toggle-flag toggle-flag
:on-close close-sub-menu}]
:preferences
[:& preferences-menu
{:layout layout
:profile profile
:toggle-flag toggle-flag
:toggle-theme toggle-theme
:on-close close-sub-menu}]
:help-info
[:& help-info-menu
{:layout layout
:on-close close-sub-menu}]
nil)]))

View file

@ -0,0 +1,101 @@
// 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
@import "refactor/common-refactor.scss";
.menu-btn {
@extend .button-tertiary;
height: $s-32;
width: calc($s-24 + $s-4);
padding: 0;
border-radius: $br-8;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
.menu {
@extend .menu-dropdown;
top: $s-48;
left: calc(var(--width, $s-256) - $s-16);
width: $s-192;
margin: 0;
}
.menu-item {
@extend .menu-item-base;
cursor: pointer;
.open-arrow {
@include flexCenter;
svg {
@extend .button-icon;
stroke: var(--icon-foreground);
}
}
&:hover {
color: var(--menu-foreground-color-hover);
.open-arrow {
svg {
stroke: var(--menu-foreground-color-hover);
}
}
.shortcut-key {
color: var(--menu-shortcut-foreground-color-hover);
}
}
}
.separator {
margin-top: $s-8;
height: $s-4;
border-top: $s-1 solid $db-secondary;
}
.shortcut {
@extend .shortcut-base;
}
.shortcut-key {
@extend .shortcut-key-base;
}
.sub-menu {
@extend .menu-dropdown;
left: calc(var(--width, $s-256) + $s-180);
width: $s-192;
min-width: calc($s-272 - $s-2);
width: 110%;
.submenu-item {
@extend .menu-item-base;
&:hover {
color: var(--menu-foreground-color-hover);
.shortcut-key {
color: var(--menu-shortcut-foreground-color-hover);
}
}
}
&.file {
top: $s-48;
}
&.edit {
top: $s-76;
}
&.view {
top: $s-116;
}
&.preferences {
top: $s-148;
}
&.help-info {
top: $s-196;
}
}

View file

@ -3442,6 +3442,12 @@ msgstr "Show fonts palette"
msgid "workspace.header.menu.undo"
msgstr "Undo"
msgid "workspace.header.menu.toggle-light-theme"
msgstr "Switch to light theme"
msgid "workspace.header.menu.toggle-dark-theme"
msgstr "Switch to dark theme"
#: src/app/main/ui/workspace/header.cljs
msgid "workspace.header.reset-zoom"
msgstr "Reset"

View file

@ -3504,6 +3504,12 @@ msgstr "Mostrar paleta de textos"
msgid "workspace.header.menu.undo"
msgstr "Deshacer"
msgid "workspace.header.menu.toggle-light-theme"
msgstr "Cambiar a tema claro"
msgid "workspace.header.menu.toggle-dark-theme"
msgstr "Cambiar a tema oscuro"
#: src/app/main/ui/workspace/header.cljs
msgid "workspace.header.reset-zoom"
msgstr "Restablecer"