mirror of
https://github.com/penpot/penpot.git
synced 2025-04-13 15:31:26 -05:00
Merge pull request #4088 from penpot/alotor-bugfixes-13
Alotor bugfixes 13
This commit is contained in:
commit
4a4aabd230
19 changed files with 1019 additions and 857 deletions
|
@ -340,30 +340,43 @@
|
|||
(grc/points->rect points))))
|
||||
|
||||
(defn content->selrect [content]
|
||||
(let [calc-extremities
|
||||
(fn [command prev]
|
||||
(case (:command command)
|
||||
:move-to [(command->point command)]
|
||||
(let [extremities
|
||||
(loop [points #{}
|
||||
from-p nil
|
||||
move-p nil
|
||||
content (seq content)]
|
||||
(if content
|
||||
(let [command (first content)
|
||||
to-p (command->point command)
|
||||
|
||||
;; If it's a line we add the beginning point and endpoint
|
||||
:line-to [(command->point prev)
|
||||
(command->point command)]
|
||||
[from-p move-p command-pts]
|
||||
(case (:command command)
|
||||
: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]]
|
||||
(when (and from-p to-p c1 c2)
|
||||
(into [from-p to-p]
|
||||
(->> (curve-extremities curve)
|
||||
(map #(curve-values curve %))))))]
|
||||
[to-p move-p []])]
|
||||
|
||||
;; We return the bezier extremities
|
||||
:curve-to (into [(command->point prev)
|
||||
(command->point command)]
|
||||
(let [curve [(command->point prev)
|
||||
(command->point command)
|
||||
(command->point command :c1)
|
||||
(command->point command :c2)]]
|
||||
(->> (curve-extremities curve)
|
||||
(map #(curve-values curve %)))))
|
||||
[]))
|
||||
(recur (apply conj points command-pts) from-p move-p (next content)))
|
||||
points))
|
||||
|
||||
extremities (mapcat calc-extremities
|
||||
content
|
||||
(concat [nil] content))]
|
||||
(grc/points->rect extremities)))
|
||||
;; 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)
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
[id objects]
|
||||
(->> (tree-seq
|
||||
#(d/not-empty? (dm/get-in objects [% :shapes]))
|
||||
#(dm/get-in objects [% :shapes])
|
||||
(fn [id]
|
||||
(let [shape (get objects id)]
|
||||
(cond->> (:shapes shape)
|
||||
(and (ctl/flex-layout? shape) (ctl/reverse? shape))
|
||||
(reverse))))
|
||||
id)
|
||||
(map #(get objects %))))
|
||||
|
||||
|
|
|
@ -756,6 +756,9 @@
|
|||
(ctl/flex-layout? shape)
|
||||
(ctl/update-flex-scale value)
|
||||
|
||||
(ctl/grid-layout? shape)
|
||||
(ctl/update-grid-scale value)
|
||||
|
||||
:always
|
||||
(ctl/update-flex-child value)))]
|
||||
|
||||
|
|
|
@ -601,6 +601,16 @@
|
|||
(d/update-in-when [:layout-padding :p3] * scale)
|
||||
(d/update-in-when [:layout-padding :p4] * scale)))
|
||||
|
||||
(defn update-grid-scale
|
||||
[shape scale]
|
||||
(letfn [(scale-track [track]
|
||||
(cond-> track
|
||||
(= (:type track) :fixed)
|
||||
(update :value * scale)))]
|
||||
(-> shape
|
||||
(update :layout-grid-columns #(mapv scale-track %))
|
||||
(update :layout-grid-rows #(mapv scale-track %)))))
|
||||
|
||||
(defn update-flex-child
|
||||
[shape scale]
|
||||
(-> shape
|
||||
|
|
|
@ -400,9 +400,21 @@
|
|||
:parent-id parent-id
|
||||
:frame-id frame-id)
|
||||
|
||||
:always
|
||||
;; Store in the meta the old id so we can do a remap afterwards
|
||||
;; in the parent
|
||||
(with-meta {::old-id (:id shape)})
|
||||
|
||||
(some? (:shapes shape))
|
||||
(assoc :shapes (mapv :id new-direct-children)))
|
||||
|
||||
;; For a GRID layout remap the cells shapes' old-id to the new id given in the clone
|
||||
new-shape
|
||||
(if (ctl/grid-layout? new-shape)
|
||||
(let [ids-map (into {} (map #(vector (-> % meta ::old-id) (:id %))) new-children)]
|
||||
(ctl/remap-grid-cells new-shape ids-map))
|
||||
new-shape)
|
||||
|
||||
new-shape (update-new-shape new-shape shape)
|
||||
new-shapes (into [new-shape] new-children)
|
||||
|
||||
|
|
|
@ -1013,20 +1013,21 @@
|
|||
(d/ordered-set)))]
|
||||
(rx/of (dws/select-shapes shapes-to-select)))
|
||||
|
||||
(let [{:keys [id type shapes]} (get objects (first selected))]
|
||||
(case type
|
||||
:text
|
||||
(rx/of (dwe/start-edition-mode id))
|
||||
(when (d/not-empty? selected)
|
||||
(let [{:keys [id type shapes]} (get objects (first selected))]
|
||||
(case type
|
||||
:text
|
||||
(rx/of (dwe/start-edition-mode id))
|
||||
|
||||
(:group :bool :frame)
|
||||
(let [shapes-ids (into (d/ordered-set) shapes)]
|
||||
(rx/of (dws/select-shapes shapes-ids)))
|
||||
(:group :bool :frame)
|
||||
(let [shapes-ids (into (d/ordered-set) shapes)]
|
||||
(rx/of (dws/select-shapes shapes-ids)))
|
||||
|
||||
:svg-raw
|
||||
nil
|
||||
:svg-raw
|
||||
nil
|
||||
|
||||
(rx/of (dwe/start-edition-mode id)
|
||||
(dwdp/start-path-edit id)))))))))
|
||||
(rx/of (dwe/start-edition-mode id)
|
||||
(dwdp/start-path-edit id))))))))))
|
||||
|
||||
(defn select-parent-layer
|
||||
[]
|
||||
|
|
|
@ -81,7 +81,8 @@
|
|||
:page-id page-id
|
||||
:rect (:selrect shape)
|
||||
:include-frames? true
|
||||
:full-frame? true})
|
||||
:full-frame? true
|
||||
:using-selrect? true})
|
||||
(rx/map #(cfh/clean-loops objects %))
|
||||
(rx/map #(dwsh/move-shapes-into-frame (:id shape) %)))
|
||||
(rx/of (dwu/commit-undo-transaction (:id shape))))
|
||||
|
|
|
@ -121,9 +121,9 @@
|
|||
pill-height (/ fcc/flex-display-pill-height zoom)
|
||||
workspace-modifiers (mf/deref refs/workspace-modifiers)
|
||||
gap-selected (mf/deref refs/workspace-gap-selected)
|
||||
hover (mf/use-var nil)
|
||||
hover-value (mf/use-var 0)
|
||||
mouse-pos (mf/use-var nil)
|
||||
hover (mf/use-state nil)
|
||||
hover-value (mf/use-state 0)
|
||||
mouse-pos (mf/use-state nil)
|
||||
padding (:layout-padding frame)
|
||||
gap (:layout-gap frame)
|
||||
{:keys [width height x1 y1]} (:selrect frame)
|
||||
|
|
|
@ -89,9 +89,9 @@
|
|||
pill-width (/ fcc/flex-display-pill-width zoom)
|
||||
pill-height (/ fcc/flex-display-pill-height zoom)
|
||||
margins-selected (mf/deref refs/workspace-margins-selected)
|
||||
hover-value (mf/use-var 0)
|
||||
mouse-pos (mf/use-var nil)
|
||||
hover (mf/use-var nil)
|
||||
hover-value (mf/use-state 0)
|
||||
mouse-pos (mf/use-state nil)
|
||||
hover (mf/use-state nil)
|
||||
hover-all? (and (not (nil? @hover)) alt?)
|
||||
hover-v? (and (or (= @hover :m1) (= @hover :m3)) shift?)
|
||||
hover-h? (and (or (= @hover :m2) (= @hover :m4)) shift?)
|
||||
|
|
|
@ -77,7 +77,6 @@
|
|||
:y (:y rect-data)
|
||||
:width (max 0 (:width rect-data))
|
||||
:height (max 0 (:height rect-data))
|
||||
|
||||
:on-pointer-enter on-pointer-enter
|
||||
:on-pointer-leave on-pointer-leave
|
||||
:on-pointer-move on-pointer-move
|
||||
|
@ -115,9 +114,9 @@
|
|||
[{:keys [frame zoom alt? shift? on-move-selected on-context-menu]}]
|
||||
(let [frame-id (:id frame)
|
||||
paddings-selected (mf/deref refs/workspace-paddings-selected)
|
||||
hover-value (mf/use-var 0)
|
||||
mouse-pos (mf/use-var nil)
|
||||
hover (mf/use-var nil)
|
||||
hover-value (mf/use-state 0)
|
||||
mouse-pos (mf/use-state nil)
|
||||
hover (mf/use-state nil)
|
||||
hover-all? (and (not (nil? @hover)) alt?)
|
||||
hover-v? (and (or (= @hover :p1) (= @hover :p3)) shift?)
|
||||
hover-h? (and (or (= @hover :p2) (= @hover :p4)) shift?)
|
||||
|
|
|
@ -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}]]]))
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
747
frontend/src/app/main/ui/workspace/main_menu.cljs
Normal file
747
frontend/src/app/main/ui/workspace/main_menu.cljs
Normal 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)]))
|
101
frontend/src/app/main/ui/workspace/main_menu.scss
Normal file
101
frontend/src/app/main/ui/workspace/main_menu.scss
Normal 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;
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.ui.workspace.shapes.text.editor
|
||||
(:require
|
||||
["draft-js" :as draft]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
|
@ -48,7 +49,7 @@
|
|||
(let [children (obj/get props "children")]
|
||||
[:span {:style {:background "#ccc" :display "inline-block"}} children]))
|
||||
|
||||
(defn render-block
|
||||
(defn- render-block
|
||||
[block shape]
|
||||
(let [type (ted/get-editor-block-type block)]
|
||||
(case type
|
||||
|
@ -59,7 +60,7 @@
|
|||
:shape shape}}
|
||||
nil)))
|
||||
|
||||
(defn styles-fn [shape styles content]
|
||||
(defn- styles-fn [shape styles content]
|
||||
(let [data (if (= (.getText content) "")
|
||||
(-> (.getData content)
|
||||
(.toJS)
|
||||
|
@ -73,19 +74,27 @@
|
|||
(def empty-editor-state
|
||||
(ted/create-editor-state nil default-decorator))
|
||||
|
||||
(defn get-blocks-to-setup [block-changes]
|
||||
(defn- get-blocks-to-setup [block-changes]
|
||||
(->> block-changes
|
||||
(filter (fn [[_ v]]
|
||||
(nil? (:old v))))
|
||||
(mapv first)))
|
||||
|
||||
(defn get-blocks-to-add-styles
|
||||
(defn- get-blocks-to-add-styles
|
||||
[block-changes]
|
||||
(->> block-changes
|
||||
(filter (fn [[_ v]]
|
||||
(and (not= (:old v) (:new v)) (= (:old v) ""))))
|
||||
(mapv first)))
|
||||
|
||||
(defn- shape->justify
|
||||
[{:keys [content]}]
|
||||
(case (d/nilv (:vertical-align content) "top")
|
||||
"center" "center"
|
||||
"top" "flex-start"
|
||||
"bottom" "flex-end"
|
||||
nil))
|
||||
|
||||
(mf/defc text-shape-edit-html
|
||||
{::mf/wrap [mf/memo]
|
||||
::mf/wrap-props false
|
||||
|
@ -247,7 +256,8 @@
|
|||
:custom-style-fn (partial styles-fn shape)
|
||||
:block-renderer-fn #(render-block % shape)
|
||||
:ref on-editor
|
||||
:editor-state state}]]))
|
||||
:editor-state state
|
||||
:style #js {:border "1px solid red"}}]]))
|
||||
|
||||
(defn translate-point-from-viewport
|
||||
"Translate a point in the viewport into client coordinates"
|
||||
|
@ -303,7 +313,19 @@
|
|||
(dm/get-prop shape :height))
|
||||
|
||||
style
|
||||
(cond-> #js {:pointer-events "all"}
|
||||
(cond-> #js {:pointerEvents "all"}
|
||||
|
||||
(not (cf/check-browser? :safari))
|
||||
(obj/merge!
|
||||
#js {:transform (dm/fmt "translate(%px, %px)" (- (dm/get-prop shape :x) x) (- (dm/get-prop shape :y) y))})
|
||||
|
||||
(cf/check-browser? :safari-17)
|
||||
(obj/merge!
|
||||
#js {:height "100%"
|
||||
:display "flex"
|
||||
:flexDirection "column"
|
||||
:justifyContent (shape->justify shape)})
|
||||
|
||||
(cf/check-browser? :safari-16)
|
||||
(obj/merge!
|
||||
#js {:position "fixed"
|
||||
|
|
|
@ -61,7 +61,8 @@
|
|||
:transform :matrix-render
|
||||
:transform-inverse :matrix-render
|
||||
:selrect :rect-render
|
||||
:points :points-render})
|
||||
:points :points-render
|
||||
:layout-grid-cells :cells-render})
|
||||
|
||||
(mf/defc shape-link
|
||||
[{:keys [id objects]}]
|
||||
|
@ -69,6 +70,25 @@
|
|||
:on-click #(st/emit! (dw/select-shape id))}
|
||||
(dm/str (dm/get-in objects [id :name]) " #" id)])
|
||||
|
||||
(mf/defc cells-render
|
||||
[{:keys [cells objects]}]
|
||||
[:div {:class (stl/css :cells-render)}
|
||||
(for [[id cell] cells]
|
||||
|
||||
[:div {:key (dm/str "cell-" id)
|
||||
:class (stl/css :cell-container)}
|
||||
[:div {:class (stl/css :cell-position)}
|
||||
(dm/fmt "(%, %) -> (%, %)"
|
||||
(:row cell)
|
||||
(:column cell)
|
||||
(+ (:row cell) (dec (:row-span cell)))
|
||||
(+ (:column cell) (dec (:column-span cell))))]
|
||||
|
||||
[:div {:class (stl/css :cell-shape)}
|
||||
(if (empty? (:shapes cell))
|
||||
[:div "<empty>"]
|
||||
[:& shape-link {:id (first (:shapes cell)) :objects objects}])]])])
|
||||
|
||||
(mf/defc debug-shape-attr
|
||||
[{:keys [attr value objects]}]
|
||||
|
||||
|
@ -79,7 +99,8 @@
|
|||
:shape-list
|
||||
[:div {:class (stl/css :shape-list)}
|
||||
(for [id value]
|
||||
[:& shape-link {:id id :objects objects}])]
|
||||
[:& shape-link {:key (dm/str "child-" id)
|
||||
:id id :objects objects}])]
|
||||
|
||||
:matrix-render
|
||||
[:div (dm/str (gmt/format-precision value 2))]
|
||||
|
@ -89,8 +110,11 @@
|
|||
|
||||
:points-render
|
||||
[:div {:class (stl/css :point-list)}
|
||||
(for [point value]
|
||||
[:div (dm/fmt "(%, %)" (:x point) (:y point))])]
|
||||
(for [[idx point] (d/enumerate value)]
|
||||
[:div {:key (dm/str "point-" idx)} (dm/fmt "(%, %)" (:x point) (:y point))])]
|
||||
|
||||
:cells-render
|
||||
[:& cells-render {:cells value :objects objects}]
|
||||
|
||||
[:div {:class (stl/css :attrs-container-value)} (str value)]))
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
display: flex;
|
||||
flex-direction: column;
|
||||
background-color: var(--panel-background-color);
|
||||
color: white;
|
||||
color: $df-primary;
|
||||
font-size: $fs-12;
|
||||
user-select: text;
|
||||
}
|
||||
|
@ -97,3 +97,8 @@
|
|||
display: flex;
|
||||
gap: $s-8;
|
||||
}
|
||||
|
||||
.cell-container {
|
||||
display: grid;
|
||||
grid-template-columns: 100px 1fr;
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Add table
Reference in a new issue