penpot/frontend/src/app/main/ui/viewer/header.cljs

286 lines
9.8 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) UXBOX Labs SL
(ns app.main.ui.viewer.header
(:require
[app.common.math :as mth]
[app.common.uuid :as uuid]
[app.config :as cfg]
[app.main.data.comments :as dcm]
[app.main.data.messages :as dm]
[app.main.data.viewer :as dv]
[app.main.data.viewer.shortcuts :as sc]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.components.fullscreen :as fs]
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [t]]
[app.util.router :as rt]
[app.util.webapi :as wapi]
[cuerdas.core :as str]
[rumext.alpha :as mf]))
(mf/defc zoom-widget
{:wrap [mf/memo]}
[{:keys [zoom
on-increase
on-decrease
on-zoom-to-50
on-zoom-to-100
on-zoom-to-200]
:as props}]
(let [show-dropdown? (mf/use-state false)]
[:div.zoom-widget {:on-click #(reset! show-dropdown? true)}
[:span {} (str (mth/round (* 100 zoom)) "%")]
[:span.dropdown-button i/arrow-down]
[:& dropdown {:show @show-dropdown?
:on-close #(reset! show-dropdown? false)}
[:ul.dropdown.zoom-dropdown
[:li {:on-click on-increase}
"Zoom in" [:span (sc/get-tooltip :increase-zoom)]]
[:li {:on-click on-decrease}
"Zoom out" [:span (sc/get-tooltip :decrease-zoom)]]
[:li {:on-click on-zoom-to-50}
"Zoom to 50%" [:span (sc/get-tooltip :zoom-50)]]
[:li {:on-click on-zoom-to-100}
"Zoom to 100%" [:span (sc/get-tooltip :reset-zoom)]]
[:li {:on-click on-zoom-to-200}
"Zoom to 200%" [:span (sc/get-tooltip :zoom-200)]]]]]))
(mf/defc share-link
[{:keys [page token] :as props}]
(let [show-dropdown? (mf/use-state false)
dropdown-ref (mf/use-ref)
locale (mf/deref i18n/locale)
create (st/emitf (dv/create-share-link))
delete (st/emitf (dv/delete-share-link))
router (mf/deref refs/router)
route (mf/deref refs/route)
link (rt/resolve router
:viewer
(:path-params route)
{:token token :index "0"})
link (str cfg/public-uri "/#" link)
copy-link
(fn [event]
(wapi/write-to-clipboard link)
(st/emit! (dm/show {:type :info
:content "Link copied successfuly!"
:timeout 3000})))]
[:*
[:span.btn-primary.btn-small
{:alt (t locale "viewer.header.share.title")
:on-click #(swap! show-dropdown? not)}
(t locale "viewer.header.share.title")]
[:& dropdown {:show @show-dropdown?
:on-close #(swap! show-dropdown? not)
:container dropdown-ref}
[:div.dropdown.share-link-dropdown {:ref dropdown-ref}
[:span.share-link-title (t locale "viewer.header.share.title")]
[:div.share-link-input
(if (string? token)
[:*
[:span.link link]
[:span.link-button {:on-click copy-link}
(t locale "viewer.header.share.copy-link")]]
[:span.link-placeholder (t locale "viewer.header.share.placeholder")])]
[:span.share-link-subtitle (t locale "viewer.header.share.subtitle")]
[:div.share-link-buttons
(if (string? token)
[:button.btn-warning {:on-click delete}
(t locale "viewer.header.share.remove-link")]
[:button.btn-primary {:on-click create}
(t locale "viewer.header.share.create-link")])]]]]))
(mf/defc interactions-menu
[{:keys [state locale] :as props}]
(let [imode (:interactions-mode state)
show-dropdown? (mf/use-state false)
show-dropdown (mf/use-fn #(reset! show-dropdown? true))
hide-dropdown (mf/use-fn #(reset! show-dropdown? false))
select-mode
(mf/use-callback
(fn [mode]
(st/emit! (dv/set-interactions-mode mode))))]
[:div.view-options
[:div.icon {:on-click #(swap! show-dropdown? not)} i/eye]
[:& dropdown {:show @show-dropdown?
:on-close hide-dropdown}
[:ul.dropdown.with-check
[:li {:class (dom/classnames :selected (= imode :hide))
:on-click #(select-mode :hide)}
[:span.icon i/tick]
[:span.label (t locale "viewer.header.dont-show-interactions")]]
[:li {:class (dom/classnames :selected (= imode :show))
:on-click #(select-mode :show)}
[:span.icon i/tick]
[:span.label (t locale "viewer.header.show-interactions")]]
[:li {:class (dom/classnames :selected (= imode :show-on-click))
:on-click #(select-mode :show-on-click)}
[:span.icon i/tick]
[:span.label (t locale "viewer.header.show-interactions-on-click")]]]]]))
(mf/defc comments-menu
[{:keys [locale] :as props}]
(let [{cmode :mode cshow :show} (mf/deref refs/comments-local)
show-dropdown? (mf/use-state false)
show-dropdown (mf/use-fn #(reset! show-dropdown? true))
hide-dropdown (mf/use-fn #(reset! show-dropdown? false))
update-mode
(mf/use-callback
(fn [mode]
(st/emit! (dcm/update-filters {:mode mode}))))
update-show
(mf/use-callback
(fn [mode]
(st/emit! (dcm/update-filters {:show mode}))))]
[:div.view-options
[:div.icon {:on-click #(swap! show-dropdown? not)} i/eye]
[:& dropdown {:show @show-dropdown?
:on-close hide-dropdown}
[:ul.dropdown.with-check
[:li {:class (dom/classnames :selected (= :all cmode))
:on-click #(update-mode :all)}
[:span.icon i/tick]
[:span.label (t locale "labels.show-all-comments")]]
[:li {:class (dom/classnames :selected (= :yours cmode))
:on-click #(update-mode :yours)}
[:span.icon i/tick]
[:span.label (t locale "labels.show-your-comments")]]
[:hr]
[:li {:class (dom/classnames :selected (= :pending cshow))
:on-click #(update-show (if (= :pending cshow) :all :pending))}
[:span.icon i/tick]
[:span.label (t locale "labels.hide-resolved-comments")]]]]]))
(mf/defc header
[{:keys [data index section state] :as props}]
(let [{:keys [project file page frames]} data
fullscreen (mf/use-ctx fs/fullscreen-context)
total (count frames)
locale (mf/deref i18n/locale)
profile (mf/deref refs/profile)
teams (mf/deref refs/teams)
team-id (get-in data [:project :team-id])
has-permission? (and (not= uuid/zero (:id profile))
(contains? teams team-id))
project-id (get-in data [:project :id])
file-id (get-in data [:file :id])
page-id (get-in data [:page :id])
on-click
(mf/use-callback
(st/emitf dv/toggle-thumbnails-panel))
on-goback
(mf/use-callback
(mf/deps project)
(st/emitf (dv/go-to-dashboard project)))
on-edit
(mf/use-callback
(mf/deps project-id file-id page-id)
(st/emitf (rt/nav :workspace
{:project-id project-id
:file-id file-id}
{:page-id page-id})))
navigate
(mf/use-callback
(mf/deps file-id page-id)
(fn [section]
(st/emit! (dv/go-to-section section))))]
[:header.viewer-header
[:div.main-icon
[:a {:on-click on-goback
;; If the user doesn't have permission we disable the link
:style {:pointer-events (when-not has-permission? "none")}} i/logo-icon]]
[:div.sitemap-zone {:alt (t locale "viewer.header.sitemap")
:on-click on-click}
[:span.project-name (:name project)]
[:span "/"]
[:span.file-name (:name file)]
[:span "/"]
[:span.page-name (:name page)]
[:span.show-thumbnails-button i/arrow-down]
[:span.counters (str (inc index) " / " total)]]
[:div.mode-zone
[:button.mode-zone-button.tooltip.tooltip-bottom
{:on-click #(navigate :interactions)
:class (dom/classnames :active (= section :interactions))
:alt "View mode"}
i/play]
(when has-permission?
[:button.mode-zone-button.tooltip.tooltip-bottom
{:on-click #(navigate :comments)
:class (dom/classnames :active (= section :comments))
:alt "Comments"}
i/chat])
[:button.mode-zone-button.tooltip.tooltip-bottom
{:on-click #(navigate :handoff)
:class (dom/classnames :active (= section :handoff))
:alt "Code mode"}
i/code]]
[:div.options-zone
(case section
:interactions [:& interactions-menu {:state state :locale locale}]
:comments [:& comments-menu {:locale locale}]
nil)
(when has-permission?
[:& share-link {:token (:token data)
:page (:page data)}])
(when has-permission?
[:a.btn-text-basic.btn-small {:on-click on-edit}
(t locale "viewer.header.edit-page")])
[:& zoom-widget
{:zoom (:zoom state)
:on-increase (st/emitf dv/increase-zoom)
:on-decrease (st/emitf dv/decrease-zoom)
:on-zoom-to-50 (st/emitf dv/zoom-to-50)
:on-zoom-to-100 (st/emitf dv/reset-zoom)
:on-zoom-to-200 (st/emitf dv/zoom-to-200)}]
[:span.btn-icon-dark.btn-small.tooltip.tooltip-bottom-left
{:alt (t locale "viewer.header.fullscreen")
:on-click #(if @fullscreen (fullscreen false) (fullscreen true))}
(if @fullscreen
i/full-screen-off
i/full-screen)]
]]))