mirror of
https://github.com/penpot/penpot.git
synced 2025-05-11 16:36:37 +02:00
✨ Improved dashboard thumbnails
This commit is contained in:
parent
5522430afe
commit
030ff398ed
9 changed files with 90 additions and 40 deletions
|
@ -9,6 +9,7 @@
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.rpc :as-alias rpc]
|
[app.rpc :as-alias rpc]
|
||||||
|
[app.rpc.commands.files :refer [resolve-public-uri]]
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[clojure.spec.alpha :as s]))
|
[clojure.spec.alpha :as s]))
|
||||||
|
@ -37,12 +38,15 @@
|
||||||
)
|
)
|
||||||
select distinct
|
select distinct
|
||||||
f.id,
|
f.id,
|
||||||
|
f.revn,
|
||||||
f.project_id,
|
f.project_id,
|
||||||
f.created_at,
|
f.created_at,
|
||||||
f.modified_at,
|
f.modified_at,
|
||||||
f.name,
|
f.name,
|
||||||
f.is_shared
|
f.is_shared,
|
||||||
|
ft.media_id
|
||||||
from file as f
|
from file as f
|
||||||
|
left join file_thumbnail as ft on (ft.file_id = f.id and ft.revn = f.revn)
|
||||||
inner join projects as pr on (f.project_id = pr.id)
|
inner join projects as pr on (f.project_id = pr.id)
|
||||||
where f.name ilike ('%' || ? || '%')
|
where f.name ilike ('%' || ? || '%')
|
||||||
and (f.deleted_at is null or f.deleted_at > now())
|
and (f.deleted_at is null or f.deleted_at > now())
|
||||||
|
@ -50,10 +54,16 @@
|
||||||
|
|
||||||
(defn search-files
|
(defn search-files
|
||||||
[conn profile-id team-id search-term]
|
[conn profile-id team-id search-term]
|
||||||
(db/exec! conn [sql:search-files
|
(->> (db/exec! conn [sql:search-files
|
||||||
profile-id team-id
|
profile-id team-id
|
||||||
profile-id team-id
|
profile-id team-id
|
||||||
search-term]))
|
search-term])
|
||||||
|
(mapv (fn [row]
|
||||||
|
(if-let [media-id (:media-id row)]
|
||||||
|
(-> row
|
||||||
|
(dissoc :media-id)
|
||||||
|
(assoc :thumbnail-uri (resolve-public-uri media-id)))
|
||||||
|
(dissoc row :media-id))))))
|
||||||
|
|
||||||
(s/def ::team-id ::us/uuid)
|
(s/def ::team-id ::us/uuid)
|
||||||
(s/def ::search-files ::us/string)
|
(s/def ::search-files ::us/string)
|
||||||
|
|
|
@ -353,3 +353,19 @@
|
||||||
(mth/max by1 y1)
|
(mth/max by1 y1)
|
||||||
(mth/min bx2 x2)
|
(mth/min bx2 x2)
|
||||||
(mth/min by2 y2)))))
|
(mth/min by2 y2)))))
|
||||||
|
(defn fix-aspect-ratio
|
||||||
|
[bounds aspect-ratio]
|
||||||
|
(if aspect-ratio
|
||||||
|
(let [width (dm/get-prop bounds :width)
|
||||||
|
height (dm/get-prop bounds :height)
|
||||||
|
target-height (* width aspect-ratio)
|
||||||
|
target-width (* height (/ 1 aspect-ratio))]
|
||||||
|
(cond-> bounds
|
||||||
|
(> target-height height)
|
||||||
|
(-> (assoc :height target-height)
|
||||||
|
(update :y - (/ (- target-height height ) 2)))
|
||||||
|
|
||||||
|
(< target-height height)
|
||||||
|
(-> (assoc :width target-width)
|
||||||
|
(update :x - (/ (- target-width width ) 2)))))
|
||||||
|
bounds))
|
||||||
|
|
|
@ -831,9 +831,15 @@
|
||||||
(ptk/reify ::set-file-thumbnail
|
(ptk/reify ::set-file-thumbnail
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(-> state
|
(letfn [(update-search-files [files]
|
||||||
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
|
(->> files
|
||||||
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)))))
|
(mapv #(cond-> %
|
||||||
|
(= file-id (:id %))
|
||||||
|
(assoc :thumbnail-uri thumbnail-uri)))))]
|
||||||
|
(-> state
|
||||||
|
(d/update-in-when [:dashboard-files file-id] assoc :thumbnail-uri thumbnail-uri)
|
||||||
|
(d/update-in-when [:dashboard-recent-files file-id] assoc :thumbnail-uri thumbnail-uri)
|
||||||
|
(d/update-when :dashboard-search-result update-search-files))))))
|
||||||
|
|
||||||
;; --- EVENT: create-file
|
;; --- EVENT: create-file
|
||||||
|
|
||||||
|
|
|
@ -66,16 +66,18 @@
|
||||||
:fill color}])
|
:fill color}])
|
||||||
|
|
||||||
(defn- calculate-dimensions
|
(defn- calculate-dimensions
|
||||||
[objects]
|
[objects aspect-ratio]
|
||||||
(let [bounds (->> (ctst/get-root-objects objects)
|
(let [bounds
|
||||||
(map (partial gsb/get-object-bounds objects))
|
(->> (ctst/get-root-objects objects)
|
||||||
(grc/join-rects))]
|
(map (partial gsb/get-object-bounds objects))
|
||||||
|
(grc/join-rects))]
|
||||||
(-> bounds
|
(-> bounds
|
||||||
(update :x mth/finite 0)
|
(update :x mth/finite 0)
|
||||||
(update :y mth/finite 0)
|
(update :y mth/finite 0)
|
||||||
(update :width mth/finite 100000)
|
(update :width mth/finite 100000)
|
||||||
(update :height mth/finite 100000)
|
(update :height mth/finite 100000)
|
||||||
(grc/update-rect :position))))
|
(grc/update-rect :position)
|
||||||
|
(grc/fix-aspect-ratio aspect-ratio))))
|
||||||
|
|
||||||
(declare shape-wrapper-factory)
|
(declare shape-wrapper-factory)
|
||||||
|
|
||||||
|
@ -194,11 +196,11 @@
|
||||||
|
|
||||||
(mf/defc page-svg
|
(mf/defc page-svg
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [data use-thumbnails embed include-metadata] :as props
|
[{:keys [data use-thumbnails embed include-metadata aspect-ratio] :as props
|
||||||
:or {embed false include-metadata false}}]
|
:or {embed false include-metadata false}}]
|
||||||
(let [objects (:objects data)
|
(let [objects (:objects data)
|
||||||
shapes (cfh/get-immediate-children objects)
|
shapes (cfh/get-immediate-children objects)
|
||||||
dim (calculate-dimensions objects)
|
dim (calculate-dimensions objects aspect-ratio)
|
||||||
vbox (format-viewbox dim)
|
vbox (format-viewbox dim)
|
||||||
bgcolor (dm/get-in data [:options :background] default-color)
|
bgcolor (dm/get-in data [:options :background] default-color)
|
||||||
|
|
||||||
|
@ -253,11 +255,14 @@
|
||||||
;; the viewer and inspector
|
;; the viewer and inspector
|
||||||
(mf/defc frame-svg
|
(mf/defc frame-svg
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [objects frame zoom use-thumbnails] :or {zoom 1} :as props}]
|
[{:keys [objects frame zoom use-thumbnails aspect-ratio background-color] :or {zoom 1} :as props}]
|
||||||
(let [frame-id (:id frame)
|
(let [frame-id (:id frame)
|
||||||
|
|
||||||
|
bgcolor (d/nilv background-color default-color)
|
||||||
include-metadata (mf/use-ctx export/include-metadata-ctx)
|
include-metadata (mf/use-ctx export/include-metadata-ctx)
|
||||||
|
|
||||||
bounds (gsb/get-object-bounds objects frame)
|
bounds (-> (gsb/get-object-bounds objects frame)
|
||||||
|
(grc/fix-aspect-ratio aspect-ratio))
|
||||||
|
|
||||||
;; Bounds without shadows/blur will be the bounds of the thumbnail
|
;; Bounds without shadows/blur will be the bounds of the thumbnail
|
||||||
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur))
|
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur))
|
||||||
|
@ -305,6 +310,7 @@
|
||||||
:xmlns "http://www.w3.org/2000/svg"
|
:xmlns "http://www.w3.org/2000/svg"
|
||||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||||
:xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
|
:xmlns:penpot (when include-metadata "https://penpot.app/xmlns")
|
||||||
|
:style {:background bgcolor}
|
||||||
:fill "none"}
|
:fill "none"}
|
||||||
[:& shape-wrapper {:shape frame}]]]))
|
[:& shape-wrapper {:shape frame}]]]))
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@
|
||||||
(rx/map (fn [styles]
|
(rx/map (fn [styles]
|
||||||
(assoc result
|
(assoc result
|
||||||
:styles styles
|
:styles styles
|
||||||
:width 250))))))
|
:width 252))))))
|
||||||
(rx/mapcat thr/render)
|
(rx/mapcat thr/render)
|
||||||
(rx/mapcat (partial persist-thumbnail file-id revn))))
|
(rx/mapcat (partial persist-thumbnail file-id revn))))
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
@import "refactor/common-refactor.scss";
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
$thumbnail-default-width: $s-252; // Default width
|
$thumbnail-default-width: $s-252; // Default width
|
||||||
$thumbnail-aspect-ration: #{2 / 3}; // Ratio 2:3
|
$thumbnail-default-height: $s-168; // Default width
|
||||||
|
|
||||||
.dashboard-grid {
|
.dashboard-grid {
|
||||||
font-size: $fs-14;
|
font-size: $fs-14;
|
||||||
|
@ -44,10 +44,12 @@ $thumbnail-aspect-ration: #{2 / 3}; // Ratio 2:3
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-item-th {
|
.grid-item-th {
|
||||||
border-radius: $br-4;
|
border-radius: $br-8;
|
||||||
text-align: initial;
|
text-align: initial;
|
||||||
width: var(--th-width, #{$thumbnail-default-width});
|
width: var(--th-width, #{$thumbnail-default-width});
|
||||||
height: calc(var(--th-width, #{$thumbnail-default-width}) * #{$thumbnail-aspect-ration});
|
height: var(--th-height, #{$thumbnail-default-height});
|
||||||
|
background-size: cover;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
img {
|
img {
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
@ -59,7 +61,7 @@ $thumbnail-aspect-ration: #{2 / 3}; // Ratio 2:3
|
||||||
outline: $br-4 solid $da-primary;
|
outline: $br-4 solid $da-primary;
|
||||||
text-align: initial;
|
text-align: initial;
|
||||||
width: calc(var(--th-width) + $s-12);
|
width: calc(var(--th-width) + $s-12);
|
||||||
height: calc(var(--th-width, #{$thumbnail-default-width}) * #{$thumbnail-aspect-ration});
|
height: var(--th-height, #{$thumbnail-default-height});
|
||||||
}
|
}
|
||||||
|
|
||||||
&.overlay {
|
&.overlay {
|
||||||
|
@ -131,10 +133,10 @@ $thumbnail-aspect-ration: #{2 / 3}; // Ratio 2:3
|
||||||
.item-badge {
|
.item-badge {
|
||||||
background-color: $da-primary;
|
background-color: $da-primary;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: $br-4;
|
border-radius: $br-6;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $s-8;
|
top: $s-12;
|
||||||
right: $s-8;
|
right: $s-12;
|
||||||
height: $s-32;
|
height: $s-32;
|
||||||
width: $s-32;
|
width: $s-32;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -258,17 +260,10 @@ $thumbnail-aspect-ration: #{2 / 3}; // Ratio 2:3
|
||||||
.grid-item-th {
|
.grid-item-th {
|
||||||
border-radius: $br-4;
|
border-radius: $br-4;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
background-position: center;
|
|
||||||
background-size: auto 80%;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
|
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
background-color: var(--canvas-color);
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
|
@ -283,8 +278,9 @@ $thumbnail-aspect-ration: #{2 / 3}; // Ratio 2:3
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg#loader-pencil {
|
:global(svg#loader-pencil) {
|
||||||
fill: $db-cuaternary;
|
stroke: $db-cuaternary;
|
||||||
|
width: calc(var(--th-width, #{$thumbnail-default-width}) * 0.25);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,7 +65,7 @@
|
||||||
text-transform: uppercase;
|
text-transform: uppercase;
|
||||||
border: $s-2 solid transparent;
|
border: $s-2 solid transparent;
|
||||||
width: var(--th-width, #{g.$thumbnail-default-width});
|
width: var(--th-width, #{g.$thumbnail-default-width});
|
||||||
height: calc(var(--th-width, #{g.$thumbnail-default-width}) * #{g.$thumbnail-aspect-ration});
|
height: var(--th-height, #{g.$thumbnail-default-height});
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
width: $s-32;
|
width: $s-32;
|
||||||
|
|
|
@ -367,13 +367,17 @@
|
||||||
limit (mth/max 1 limit)
|
limit (mth/max 1 limit)
|
||||||
|
|
||||||
th-size (when width
|
th-size (when width
|
||||||
(- (/ (- width 32 (* (dec limit) 24)) limit) 12))]
|
(mth/floor (- (/ (- width 32 (* (dec limit) 24)) limit) 12)))
|
||||||
|
|
||||||
|
;; Need an even value
|
||||||
|
th-size (if (odd? (int th-size)) (- th-size 1) th-size)]
|
||||||
|
|
||||||
(mf/with-effect
|
(mf/with-effect
|
||||||
[th-size]
|
[th-size]
|
||||||
(when th-size
|
(when th-size
|
||||||
(let [node (mf/ref-val rowref)]
|
(let [node (mf/ref-val rowref)]
|
||||||
(.setProperty (.-style node) "--th-width" (str th-size "px")))))
|
(.setProperty (.-style node) "--th-width" (str th-size "px"))
|
||||||
|
(.setProperty (.-style node) "--th-height" (str (mth/ceil (* th-size (/ 2 3))) "px")))))
|
||||||
|
|
||||||
(mf/with-effect []
|
(mf/with-effect []
|
||||||
(let [node (mf/ref-val rowref)
|
(let [node (mf/ref-val rowref)
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.worker.thumbnails
|
(ns app.worker.thumbnails
|
||||||
(:require
|
(:require
|
||||||
["react-dom/server" :as rds]
|
["react-dom/server" :as rds]
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.uri :as u]
|
[app.common.uri :as u]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
|
@ -62,16 +63,27 @@
|
||||||
(binding [fonts/loaded-hints (l/atom #{})]
|
(binding [fonts/loaded-hints (l/atom #{})]
|
||||||
(let [objects (:objects page)
|
(let [objects (:objects page)
|
||||||
frame (some->> page :thumbnail-frame-id (get objects))
|
frame (some->> page :thumbnail-frame-id (get objects))
|
||||||
|
background-color (dm/get-in page [:options :background])
|
||||||
element (if frame
|
element (if frame
|
||||||
(mf/element render/frame-svg #js {:objects objects :frame frame :use-thumbnails true})
|
(mf/element render/frame-svg #js
|
||||||
(mf/element render/page-svg #js {:data page :use-thumbnails true :embed true}))
|
{:objects objects
|
||||||
|
:frame frame
|
||||||
|
:use-thumbnails true
|
||||||
|
:background-color background-color
|
||||||
|
:aspect-ratio (/ 2 3)})
|
||||||
|
|
||||||
|
(mf/element render/page-svg #js
|
||||||
|
{:data page
|
||||||
|
:use-thumbnails true
|
||||||
|
:embed true
|
||||||
|
:aspect-ratio (/ 2 3)}))
|
||||||
data (rds/renderToStaticMarkup element)]
|
data (rds/renderToStaticMarkup element)]
|
||||||
{:data data
|
{:data data
|
||||||
:fonts @fonts/loaded-hints
|
:fonts @fonts/loaded-hints
|
||||||
:file-id file-id
|
:file-id file-id
|
||||||
:revn revn}))
|
:revn revn}))
|
||||||
(catch :default cause
|
(catch :default cause
|
||||||
(js/console.error "unexpected erorr on rendering thumbnail" cause)
|
(js/console.error "unexpected error on rendering thumbnail" cause)
|
||||||
nil)))
|
nil)))
|
||||||
|
|
||||||
(defmethod impl/handler :thumbnails/generate-for-file
|
(defmethod impl/handler :thumbnails/generate-for-file
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue