penpot/frontend/src/app/main/ui/viewer/thumbnails.cljs
2024-04-03 17:44:38 +02:00

149 lines
5.4 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.viewer.thumbnails
(:require-macros [app.main.style :as stl])
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.helpers :as cfh]
[app.common.geom.shapes :as gsh]
[app.main.data.viewer :as dv]
[app.main.render :as render]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [tr]]
[app.util.object :as obj]
[app.util.timers :as ts]
[rumext.v2 :as mf]))
(mf/defc thumbnails-content
[{:keys [children expanded? total] :as props}]
(let [container (mf/use-ref)
width (mf/use-var (.. js/document -documentElement -clientWidth))
element-width (mf/use-var 152)
offset (mf/use-state 0)
on-left-arrow-click
(fn [_]
(swap! offset (fn [v]
(if (pos? v)
(dec v)
v))))
on-right-arrow-click
(fn [_]
(let [visible (/ @width @element-width)
max-val (- total visible)]
(swap! offset (fn [v]
(if (< v max-val)
(inc v)
v)))))
on-scroll
(fn [event]
(if (pos? (.. event -nativeEvent -deltaY))
(on-right-arrow-click event)
(on-left-arrow-click event)))
on-mount
(fn []
(let [dom (mf/ref-val container)]
(reset! width (obj/get dom "clientWidth"))))]
(mf/use-effect on-mount)
(if expanded?
[:div {:class (stl/css :thumbnails-content)}
[:div {:class (stl/css :thumbnails-list-expanded)} children]]
[:div {:class (stl/css :thumbnails-content)}
[:button {:class (stl/css :left-scroll-handler)
:on-click on-left-arrow-click} i/arrow]
[:button {:class (stl/css :right-scroll-handler)
:on-click on-right-arrow-click} i/arrow]
[:div {:class (stl/css :thumbnails-list)
:ref container
:on-wheel on-scroll}
[:div {:class (stl/css :thumbnails-list-inside)
:style {:right (str (* @offset 152) "px")}}
children]]])))
(mf/defc thumbnails-summary
[{:keys [on-toggle-expand on-close total] :as props}]
[:div {:class (stl/css :thumbnails-summary)}
[:span {:class (stl/css :counter)}
(tr "labels.num-of-frames" (i18n/c total))]
[:span {:class (stl/css :actions)}
[:button {:class (stl/css :expand-btn)
:on-click on-toggle-expand} i/arrow]
[:button {:class (stl/css :close-btn)
:on-click on-close} i/close]]])
(mf/defc thumbnail-item
{::mf/wrap [mf/memo
#(mf/deferred % ts/idle-then-raf)]}
[{:keys [selected? frame on-click index objects page-id thumbnail-data]}]
(let [children-ids (cfh/get-children-ids objects (:id frame))
children-bounds (gsh/shapes->rect (concat [frame] (->> children-ids (keep (d/getf objects)))))]
[:button {:class (stl/css :thumbnail-item)
:on-click #(on-click % index)}
[:div {:class (stl/css-case :thumbnail-preview true
:selected selected?)}
[:& render/frame-svg {:frame (-> frame
(assoc :thumbnail (get thumbnail-data (dm/str page-id (:id frame))))
(assoc :children-bounds children-bounds))
:objects objects
:use-thumbnails true}]]
[:div {:class (stl/css :thumbnail-info)
:title (:name frame)}
(:name frame)]]))
(mf/defc thumbnails-panel
[{:keys [frames page index show? thumbnail-data] :as props}]
(let [expanded-state (mf/use-state false)
expanded? (deref expanded-state)
container (mf/use-ref)
objects (:objects page)
on-close #(st/emit! dv/toggle-thumbnails-panel)
selected (mf/use-var false)
on-item-click
(mf/use-fn
(mf/deps expanded?)
(fn [_ index]
(compare-and-set! selected false true)
(st/emit! (dv/go-to-frame-by-index index))
(when expanded?
(on-close))))
toggle-expand
(mf/use-fn
#(swap! expanded-state not))]
[:section {:class (stl/css-case :viewer-thumbnails true
:expanded expanded?)
;; This is better as an inline-style so it won't make a reflow of every frame inside
:style {:display (when (not show?) "none")}
:ref container}
[:& thumbnails-summary {:on-toggle-expand toggle-expand
:on-close on-close
:total (count frames)}]
[:& thumbnails-content {:expanded? expanded?
:total (count frames)}
(for [[i frame] (d/enumerate frames)]
[:& thumbnail-item {:index i
:key (dm/str (:id frame) "-" i)
:frame frame
:page-id (:id page)
:objects objects
:on-click on-item-click
:selected? (= i index)
:thumbnail-data thumbnail-data}])]]))