penpot/frontend/src/app/main/ui/workspace/right_header.cljs
Andrey Antukh 3e090b126e ♻️ Refactor application routing
Mainly removes an inconsistent use of path params and normalize
all routes to use query params for make it extensible without
breaking urls.
2024-12-03 18:23:41 +01:00

265 lines
9.0 KiB
Clojure

;; 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.right-header
(:require-macros [app.main.style :as stl])
(:require
[app.config :as cf]
[app.main.data.common :as dcm]
[app.main.data.event :as ev]
[app.main.data.modal :as modal]
[app.main.data.shortcuts :as scd]
[app.main.data.workspace :as dw]
[app.main.data.workspace.drawing.common :as dwc]
[app.main.data.workspace.shortcuts :as sc]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.context :as ctx]
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
[app.main.ui.exports.assets :refer [export-progress-widget]]
[app.main.ui.formats :as fmt]
[app.main.ui.icons :as i]
[app.main.ui.workspace.presence :refer [active-sessions]]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]]
[okulary.core :as l]
[rumext.v2 :as mf]))
(def ref:persistence-status
(l/derived :status refs/persistence))
;; --- Persistence state Widget
(mf/defc persistence-state-widget
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[]
(let [status (mf/deref ref:persistence-status)
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)]
(when-not workspace-read-only?
[:div {:class (stl/css :persistence-status-widget)}
(case status
:pending
[:div {:class (stl/css :status-icon :pending-status)
:title (tr "workspace.header.unsaved")}
i/status-alert]
:saving
[:div {:class (stl/css :status-icon :pending-status)
:title (tr "workspace.header.unsaved")}
i/status-alert]
:saved
[:div {:class (stl/css :status-icon :saved-status)
:title (tr "workspace.header.saved")}
i/status-tick]
:error
[:div {:class (stl/css :status-icon :error-status)
:title "There was an error saving the data. Please refresh if this persists."}
i/status-wrong]
nil)])))
;; --- Zoom Widget
(mf/defc zoom-widget-workspace
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[{:keys [zoom on-increase on-decrease on-zoom-reset on-zoom-fit on-zoom-selected]}]
(let [open* (mf/use-state false)
open? (deref open*)
open-dropdown
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! open* true)))
close-dropdown
(mf/use-fn
(fn [event]
(dom/stop-propagation event)
(reset! open* false)))
on-increase
(mf/use-fn
(mf/deps on-increase)
(fn [event]
(dom/stop-propagation event)
(on-increase)))
on-decrease
(mf/use-fn
(mf/deps on-decrease)
(fn [event]
(dom/stop-propagation event)
(on-decrease)))
zoom (fmt/format-percent zoom {:precision 0})]
[:*
[:div {:on-click open-dropdown
:class (stl/css-case :zoom-widget true
:selected open?)
:title (tr "workspace.header.zoom")}
[:span {:class (stl/css :label)} zoom]]
[:& dropdown {:show open? :on-close close-dropdown}
[:ul {:class (stl/css :dropdown)}
[:li {:class (stl/css :basic-zoom-bar)}
[:span {:class (stl/css :zoom-btns)}
[:> icon-button* {:variant "ghost"
:aria-label (tr "shortcuts.decrease-zoom")
:on-click on-decrease
:icon "remove"}]
[:p {:class (stl/css :zoom-text)} zoom]
[:> icon-button* {:variant "ghost"
:aria-label (tr "shortcuts.increase-zoom")
:on-click on-increase
:icon "add"}]]
[:button {:class (stl/css :reset-btn)
:on-click on-zoom-reset}
(tr "workspace.header.reset-zoom")]]
[:li {:class (stl/css :zoom-option)
:on-click on-zoom-fit}
(tr "workspace.header.zoom-fit-all")
[:span {:class (stl/css :shortcuts)}
(for [sc (scd/split-sc (sc/get-tooltip :fit-all))]
[:span {:class (stl/css :shortcut-key)
:key (str "zoom-fit-" sc)} sc])]]
[:li {:class (stl/css :zoom-option)
:on-click on-zoom-selected}
(tr "workspace.header.zoom-selected")
[:span {:class (stl/css :shortcuts)}
(for [sc (scd/split-sc (sc/get-tooltip :zoom-selected))]
[:span {:class (stl/css :shortcut-key)
:key (str "zoom-selected-" sc)} sc])]]]]]))
;; --- Header Component
(mf/defc right-header
{::mf/wrap-props false}
[{:keys [file layout page-id]}]
(let [file-id (:id file)
zoom (mf/deref refs/selected-zoom)
read-only? (mf/use-ctx ctx/workspace-read-only?)
selected-drawtool (mf/deref refs/selected-drawing-tool)
on-increase (mf/use-fn #(st/emit! (dw/increase-zoom nil)))
on-decrease (mf/use-fn #(st/emit! (dw/decrease-zoom nil)))
on-zoom-reset (mf/use-fn #(st/emit! dw/reset-zoom))
on-zoom-fit (mf/use-fn #(st/emit! dw/zoom-to-fit-all))
on-zoom-selected (mf/use-fn #(st/emit! dw/zoom-to-selected-shape))
editing* (mf/use-state false)
editing? (deref editing*)
input-ref (mf/use-ref nil)
team (mf/deref refs/team)
nav-to-viewer
(mf/use-fn
(mf/deps file-id page-id)
(fn []
(let [params {:page-id page-id
:file-id file-id
:section "interactions"}]
(st/emit! (dcm/go-to-viewer params)))))
active-comments
(mf/use-fn
(mf/deps layout)
(fn []
(st/emit! :interrupt
(dw/clear-edition-mode)
(-> (dw/remove-layout-flag :document-history)
(vary-meta assoc ::ev/origin "workspace-header"))
(dw/select-for-drawing :comments))))
toggle-comments
(mf/use-fn
(mf/deps selected-drawtool)
(fn [_]
(if (= selected-drawtool :comments)
(st/emit! (dwc/clear-drawing))
(active-comments))))
toggle-history
(mf/use-fn
(mf/deps selected-drawtool)
(fn []
(when (= :comments selected-drawtool)
(st/emit! :interrupt
(dw/clear-edition-mode)))
(st/emit! (-> (dw/toggle-layout-flag :document-history)
(vary-meta assoc ::ev/origin "workspace-header")))))
open-share-dialog
(mf/use-fn
(mf/deps team)
(fn []
(st/emit! (modal/show {:type :invite-members
:team team
:origin :workspace}))))]
(mf/with-effect [editing?]
(when ^boolean editing?
(dom/select-text! (mf/ref-val input-ref))))
[:div {:class (stl/css :workspace-header-right)}
[:div {:class (stl/css :users-section)}
[:& active-sessions]]
[:& persistence-state-widget]
[:& export-progress-widget]
[:div {:class (stl/css :separator)}]
[:div {:class (stl/css :zoom-section)}
[:& zoom-widget-workspace
{:zoom zoom
:on-increase on-increase
:on-decrease on-decrease
:on-zoom-reset on-zoom-reset
:on-zoom-fit on-zoom-fit
:on-zoom-selected on-zoom-selected}]]
[:div {:class (stl/css :comments-section)}
[:button {:title (tr "workspace.toolbar.comments" (sc/get-tooltip :add-comment))
:aria-label (tr "workspace.toolbar.comments" (sc/get-tooltip :add-comment))
:class (stl/css-case :comments-btn true
:selected (= selected-drawtool :comments))
:on-click toggle-comments
:data-tool "comments"}
i/comments]]
(when-not ^boolean read-only?
[:div {:class (stl/css :history-section)}
[:button
{:title (tr "workspace.sidebar.history" (sc/get-tooltip :toggle-history))
:aria-label (tr "workspace.sidebar.history" (sc/get-tooltip :toggle-history))
:class (stl/css-case :selected (contains? layout :document-history)
:history-button true)
:on-click toggle-history}
i/history]])
(when (cf/external-feature-flag "share-01" "test")
[:a {:class (stl/css :viewer-btn)
:title (tr "workspace.header.share")
:on-click open-share-dialog}
i/share])
[:a {:class (stl/css :viewer-btn)
:title (tr "workspace.header.viewer" (sc/get-tooltip :open-viewer))
:on-click nav-to-viewer}
i/play]]))