mirror of
https://github.com/penpot/penpot.git
synced 2025-07-25 21:47:22 +02:00
commit
17d28ed9bc
13 changed files with 83 additions and 51 deletions
|
@ -95,8 +95,8 @@
|
||||||
(defn- find-filename-candidate
|
(defn- find-filename-candidate
|
||||||
[params used]
|
[params used]
|
||||||
(loop [index 0]
|
(loop [index 0]
|
||||||
(let [candidate (str (str/slug (:name params))
|
(let [candidate (str (:name params)
|
||||||
(str/trim (str/blank? (:suffix params "")))
|
(:suffix params "")
|
||||||
(when (pos? index)
|
(when (pos? index)
|
||||||
(str "-" (inc index)))
|
(str "-" (inc index)))
|
||||||
(case (:type params)
|
(case (:type params)
|
||||||
|
|
|
@ -62,8 +62,8 @@
|
||||||
(p/let [content (screenshot-object browser params)]
|
(p/let [content (screenshot-object browser params)]
|
||||||
{:content content
|
{:content content
|
||||||
:filename (or (:filename params)
|
:filename (or (:filename params)
|
||||||
(str (str/slug (:name params))
|
(str (:name params)
|
||||||
(str/trim (:suffix params ""))
|
(:suffix params "")
|
||||||
(case (:type params)
|
(case (:type params)
|
||||||
:png ".png"
|
:png ".png"
|
||||||
:jpeg ".jpg")))
|
:jpeg ".jpg")))
|
||||||
|
|
|
@ -261,8 +261,8 @@
|
||||||
(p/let [content (render-object browser params)]
|
(p/let [content (render-object browser params)]
|
||||||
{:content content
|
{:content content
|
||||||
:filename (or (:filename params)
|
:filename (or (:filename params)
|
||||||
(str (str/slug (:name params))
|
(str (:name params)
|
||||||
(str/trim (:suffix params ""))
|
(:suffix params "")
|
||||||
".svg"))
|
".svg"))
|
||||||
:length (alength content)
|
:length (alength content)
|
||||||
:mime-type "image/svg+xml"}))
|
:mime-type "image/svg+xml"}))
|
||||||
|
|
|
@ -4092,10 +4092,10 @@
|
||||||
"workspace.toolbar.path" : {
|
"workspace.toolbar.path" : {
|
||||||
"used-in" : [ "src/app/main/ui/workspace/left_toolbar.cljs:98" ],
|
"used-in" : [ "src/app/main/ui/workspace/left_toolbar.cljs:98" ],
|
||||||
"translations" : {
|
"translations" : {
|
||||||
"en" : "Path",
|
"en" : "Path (P)",
|
||||||
"fr" : "Chemin",
|
"fr" : "Chemin (P)",
|
||||||
"ru" : "Линия",
|
"ru" : "Линия (P)",
|
||||||
"es" : "Ruta"
|
"es" : "Ruta (P)"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"workspace.toolbar.rect" : {
|
"workspace.toolbar.rect" : {
|
||||||
|
|
|
@ -277,7 +277,7 @@
|
||||||
border-color: $color-black;
|
border-color: $color-black;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clear-search {
|
.search, .clear-search {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -289,7 +289,6 @@
|
||||||
svg {
|
svg {
|
||||||
fill: $color-gray-30;
|
fill: $color-gray-30;
|
||||||
height: 15px;
|
height: 15px;
|
||||||
transform: rotate(45deg);
|
|
||||||
width: 15px;
|
width: 15px;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
@ -297,6 +296,10 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.clear-search svg {
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.profile-bar {
|
&.profile-bar {
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
[{:keys [status body] :as response}]
|
[{:keys [status body] :as response}]
|
||||||
(cond
|
(cond
|
||||||
(= 204 status)
|
(= 204 status)
|
||||||
(rx/empty)
|
;; We need to send "something" so the streams listening downstream can act
|
||||||
|
(rx/of :empty)
|
||||||
|
|
||||||
(= 502 status)
|
(= 502 status)
|
||||||
(rx/throw {:type :bad-gateway})
|
(rx/throw {:type :bad-gateway})
|
||||||
|
|
|
@ -75,12 +75,14 @@
|
||||||
(mf/defc sidebar-search
|
(mf/defc sidebar-search
|
||||||
[{:keys [search-term team-id locale] :as props}]
|
[{:keys [search-term team-id locale] :as props}]
|
||||||
(let [search-term (or search-term "")
|
(let [search-term (or search-term "")
|
||||||
|
focused? (mf/use-state false)
|
||||||
emit! (mf/use-memo #(f/debounce st/emit! 500))
|
emit! (mf/use-memo #(f/debounce st/emit! 500))
|
||||||
|
|
||||||
on-search-focus
|
on-search-focus
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps team-id)
|
(mf/deps team-id)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
(reset! focused? true)
|
||||||
(let [target (dom/get-target event)
|
(let [target (dom/get-target event)
|
||||||
value (dom/get-value target)]
|
value (dom/get-value target)]
|
||||||
(dom/select-text! target)
|
(dom/select-text! target)
|
||||||
|
@ -88,6 +90,11 @@
|
||||||
(emit! (rt/nav :dashboard-search {:team-id team-id} {}))
|
(emit! (rt/nav :dashboard-search {:team-id team-id} {}))
|
||||||
(emit! (rt/nav :dashboard-search {:team-id team-id} {:search-term value}))))))
|
(emit! (rt/nav :dashboard-search {:team-id team-id} {:search-term value}))))))
|
||||||
|
|
||||||
|
on-search-blur
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [event]
|
||||||
|
(reset! focused? false)))
|
||||||
|
|
||||||
on-search-change
|
on-search-change
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps team-id)
|
(mf/deps team-id)
|
||||||
|
@ -114,11 +121,18 @@
|
||||||
:default-value search-term
|
:default-value search-term
|
||||||
:auto-complete "off"
|
:auto-complete "off"
|
||||||
:on-focus on-search-focus
|
:on-focus on-search-focus
|
||||||
|
:on-blur on-search-blur
|
||||||
:on-change on-search-change
|
:on-change on-search-change
|
||||||
:ref #(when % (set! (.-value %) search-term))}]
|
:ref #(when % (set! (.-value %) search-term))}]
|
||||||
[:div.clear-search
|
|
||||||
{:on-click on-clear-click}
|
(if (or @focused? (not (empty? search-term)))
|
||||||
i/close]]))
|
[:div.clear-search
|
||||||
|
{:on-click on-clear-click}
|
||||||
|
i/close]
|
||||||
|
|
||||||
|
[:div.search
|
||||||
|
{:on-click on-clear-click}
|
||||||
|
i/search])]))
|
||||||
|
|
||||||
(mf/defc teams-selector-dropdown
|
(mf/defc teams-selector-dropdown
|
||||||
[{:keys [team profile locale] :as props}]
|
[{:keys [team profile locale] :as props}]
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.util.i18n :refer [t]]
|
[app.util.i18n :refer [t]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.util.code-gen :as cg]
|
[app.util.code-gen :as cg]
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]))
|
[app.main.ui.components.copy-button :refer [copy-button]]))
|
||||||
|
@ -20,18 +21,6 @@
|
||||||
(defn has-image? [shape]
|
(defn has-image? [shape]
|
||||||
(and (= (:type shape) :image)))
|
(and (= (:type shape) :image)))
|
||||||
|
|
||||||
(defn mtype->extension [mtype]
|
|
||||||
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
|
|
||||||
(case mtype
|
|
||||||
"image/apng" "apng"
|
|
||||||
"image/avif" "avif"
|
|
||||||
"image/gif" "gif"
|
|
||||||
"image/jpeg" "jpg"
|
|
||||||
"image/png" "png"
|
|
||||||
"image/svg+xml" "svg"
|
|
||||||
"image/webp" "webp"
|
|
||||||
nil))
|
|
||||||
|
|
||||||
(mf/defc image-panel [{:keys [shapes locale]}]
|
(mf/defc image-panel [{:keys [shapes locale]}]
|
||||||
(let [shapes (->> shapes (filter has-image?))]
|
(let [shapes (->> shapes (filter has-image?))]
|
||||||
(for [shape shapes]
|
(for [shape shapes]
|
||||||
|
@ -52,7 +41,7 @@
|
||||||
|
|
||||||
(let [mtype (-> shape :metadata :mtype)
|
(let [mtype (-> shape :metadata :mtype)
|
||||||
name (:name shape)
|
name (:name shape)
|
||||||
extension (mtype->extension mtype)]
|
extension (dom/mtype->extension mtype)]
|
||||||
[:a.download-button {:target "_blank"
|
[:a.download-button {:target "_blank"
|
||||||
:download (if extension
|
:download (if extension
|
||||||
(str name "." extension)
|
(str name "." extension)
|
||||||
|
|
|
@ -205,21 +205,22 @@
|
||||||
(let [center-x (+ x1 (/ (- x2 x1) 2))
|
(let [center-x (+ x1 (/ (- x2 x1) 2))
|
||||||
center-y (+ y1 (/ (- y2 y1) 2))
|
center-y (+ y1 (/ (- y2 y1) 2))
|
||||||
distance (gpt/distance (gpt/point x1 y1) (gpt/point x2 y2))]
|
distance (gpt/distance (gpt/point x1 y1) (gpt/point x2 y2))]
|
||||||
[:g.distance-line {:key (str "line-%s-%s-%s-%s" x1 y1 x2 y2)}
|
(when-not (mth/almost-zero? distance)
|
||||||
[:line
|
[:g.distance-line {:key (str "line-%s-%s-%s-%s" x1 y1 x2 y2)}
|
||||||
{:x1 x1
|
[:line
|
||||||
:y1 y1
|
{:x1 x1
|
||||||
:x2 x2
|
:y1 y1
|
||||||
:y2 y2
|
:x2 x2
|
||||||
:style {:stroke distance-color
|
:y2 y2
|
||||||
:stroke-width distance-line-stroke}}]
|
:style {:stroke distance-color
|
||||||
|
:stroke-width distance-line-stroke}}]
|
||||||
|
|
||||||
[:& distance-display-pill
|
[:& distance-display-pill
|
||||||
{:x center-x
|
{:x center-x
|
||||||
:y center-y
|
:y center-y
|
||||||
:zoom zoom
|
:zoom zoom
|
||||||
:distance (str (mth/round distance) "px")
|
:distance (str (mth/round distance) "px")
|
||||||
:bounds bounds}]]))))
|
:bounds bounds}]])))))
|
||||||
|
|
||||||
(mf/defc selection-guides [{:keys [bounds selrect zoom]}]
|
(mf/defc selection-guides [{:keys [bounds selrect zoom]}]
|
||||||
[:g.selection-guides
|
[:g.selection-guides
|
||||||
|
@ -233,11 +234,12 @@
|
||||||
:stroke-dasharray (/ select-guide-dasharray zoom)}}])])
|
:stroke-dasharray (/ select-guide-dasharray zoom)}}])])
|
||||||
|
|
||||||
(mf/defc measurement [{:keys [bounds frame selected-shapes hover-shape zoom]}]
|
(mf/defc measurement [{:keys [bounds frame selected-shapes hover-shape zoom]}]
|
||||||
(let [selected-selrect (gsh/selection-rect selected-shapes)
|
(let [selected-ids (into #{} (map :id) selected-shapes)
|
||||||
|
selected-selrect (gsh/selection-rect selected-shapes)
|
||||||
hover-selrect (:selrect hover-shape)
|
hover-selrect (:selrect hover-shape)
|
||||||
bounds-selrect (bound->selrect bounds)]
|
bounds-selrect (bound->selrect bounds)]
|
||||||
|
|
||||||
(when (seq selected-shapes)
|
(when (and (seq selected-shapes) (not (contains? selected-ids (:id hover-shape))))
|
||||||
[:g.measurement-feedback {:pointer-events "none"}
|
[:g.measurement-feedback {:pointer-events "none"}
|
||||||
[:& selection-guides {:selrect selected-selrect :bounds bounds :zoom zoom}]
|
[:& selection-guides {:selrect selected-selrect :bounds bounds :zoom zoom}]
|
||||||
[:& size-display {:selrect selected-selrect :zoom zoom}]
|
[:& size-display {:selrect selected-selrect :zoom zoom}]
|
||||||
|
|
|
@ -115,7 +115,9 @@
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
grow-type (unchecked-get props "grow-type")
|
grow-type (unchecked-get props "grow-type")
|
||||||
embed-fonts? (mf/use-ctx muc/embed-ctx)
|
embed-fonts? (mf/use-ctx muc/embed-ctx)
|
||||||
{:keys [id x y width height content]} shape]
|
{:keys [id x y width height content]} shape
|
||||||
|
;; We add 8px to add a padding for the exporter
|
||||||
|
width (+ width 8)]
|
||||||
[:foreignObject {:x x
|
[:foreignObject {:x x
|
||||||
:y y
|
:y y
|
||||||
:id (:id shape)
|
:id (:id shape)
|
||||||
|
|
|
@ -35,11 +35,15 @@
|
||||||
:exports exports}}))
|
:exports exports}}))
|
||||||
|
|
||||||
(defn- trigger-download
|
(defn- trigger-download
|
||||||
[name blob]
|
[filename blob]
|
||||||
(let [link (dom/create-element "a")
|
(let [link (dom/create-element "a")
|
||||||
uri (dom/create-uri blob)]
|
uri (dom/create-uri blob)
|
||||||
|
extension (dom/mtype->extension (.-type ^js blob))
|
||||||
|
filename (if extension
|
||||||
|
(str filename "." extension)
|
||||||
|
filename)]
|
||||||
(obj/set! link "href" uri)
|
(obj/set! link "href" uri)
|
||||||
(obj/set! link "download" (str/slug name))
|
(obj/set! link "download" filename)
|
||||||
(obj/set! (.-style ^js link) "display" "none")
|
(obj/set! (.-style ^js link) "display" "none")
|
||||||
(.appendChild (.-body ^js js/document) link)
|
(.appendChild (.-body ^js js/document) link)
|
||||||
(.click link)
|
(.click link)
|
||||||
|
@ -51,6 +55,11 @@
|
||||||
exports (:exports shape [])
|
exports (:exports shape [])
|
||||||
loading? (mf/use-state false)
|
loading? (mf/use-state false)
|
||||||
|
|
||||||
|
filename (cond-> (:name shape)
|
||||||
|
(and (= (count exports) 1)
|
||||||
|
(not (empty (:suffix (first exports)))))
|
||||||
|
(str (:suffix (first exports))))
|
||||||
|
|
||||||
on-download
|
on-download
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps shape)
|
(mf/deps shape)
|
||||||
|
@ -62,7 +71,7 @@
|
||||||
(fn [{:keys [status body] :as response}]
|
(fn [{:keys [status body] :as response}]
|
||||||
(js/console.log status body)
|
(js/console.log status body)
|
||||||
(if (= status 200)
|
(if (= status 200)
|
||||||
(trigger-download (:name shape) body)
|
(trigger-download filename body)
|
||||||
(st/emit! (dm/error (tr "errors.unexpected-error")))))
|
(st/emit! (dm/error (tr "errors.unexpected-error")))))
|
||||||
(constantly nil)
|
(constantly nil)
|
||||||
(fn []
|
(fn []
|
||||||
|
|
|
@ -257,3 +257,15 @@
|
||||||
|
|
||||||
(defn get-data [^js node ^string attr]
|
(defn get-data [^js node ^string attr]
|
||||||
(.getAttribute node (str "data-" attr)))
|
(.getAttribute node (str "data-" attr)))
|
||||||
|
|
||||||
|
(defn mtype->extension [mtype]
|
||||||
|
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
|
||||||
|
(case mtype
|
||||||
|
"image/apng" "apng"
|
||||||
|
"image/avif" "avif"
|
||||||
|
"image/gif" "gif"
|
||||||
|
"image/jpeg" "jpg"
|
||||||
|
"image/png" "png"
|
||||||
|
"image/svg+xml" "svg"
|
||||||
|
"image/webp" "webp"
|
||||||
|
nil))
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
(def ^:private cancel-idle-callback #(js/cancelIdleCallback %)))
|
(def ^:private cancel-idle-callback #(js/cancelIdleCallback %)))
|
||||||
(do
|
(do
|
||||||
(def ^:private request-idle-callback #(js/setTimeout % 100))
|
(def ^:private request-idle-callback #(js/setTimeout % 100))
|
||||||
(def ^:private cancel-idle-callback #(js/cancelTimeout %))))
|
(def ^:private cancel-idle-callback #(js/clearTimeout %))))
|
||||||
|
|
||||||
(defn schedule-on-idle
|
(defn schedule-on-idle
|
||||||
[func]
|
[func]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue