mirror of
https://github.com/penpot/penpot.git
synced 2025-05-07 20:45:53 +02:00
✨ Adds multiselection attributes
This commit is contained in:
parent
d6573c2bcc
commit
b52289860f
23 changed files with 831 additions and 612 deletions
|
@ -214,3 +214,36 @@
|
|||
(concat new-children new-child-objects)
|
||||
(concat updated-children updated-child-objects))))))))
|
||||
|
||||
|
||||
(defn indexed-shapes
|
||||
"Retrieves a list with the indexes for each element in the layer tree.
|
||||
This will be used for shift+selection."
|
||||
[objects]
|
||||
(let [rec-index
|
||||
(fn rec-index [cur-idx id]
|
||||
(let [object (get objects id)
|
||||
red-fn
|
||||
(fn [cur-idx id]
|
||||
(let [[prev-idx _] (first cur-idx)
|
||||
prev-idx (or prev-idx 0)
|
||||
cur-idx (conj cur-idx [(inc prev-idx) id])]
|
||||
(rec-index cur-idx id)))]
|
||||
(reduce red-fn cur-idx (reverse (:shapes object)))))]
|
||||
(into {} (rec-index '() uuid/zero))))
|
||||
|
||||
|
||||
(defn expand-region-selection
|
||||
"Given a selection selects all the shapes between the first and last in
|
||||
an indexed manner (shift selection)"
|
||||
[objects selection]
|
||||
(let [indexed-shapes (indexed-shapes objects)
|
||||
filter-indexes (->> indexed-shapes
|
||||
(filter (comp selection second))
|
||||
(map first))
|
||||
|
||||
from (apply min filter-indexes)
|
||||
to (apply max filter-indexes)]
|
||||
(->> indexed-shapes
|
||||
(filter (fn [[idx _]] (and (>= idx from) (<= idx to))))
|
||||
(map second)
|
||||
(into #{}))))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 500 500" fill="none">
|
||||
<rect width="484.4" height="484.4" x="-492" y="-492" stroke="#64666a" stroke-width="15.6" rx="242.2" transform="scale(-1)"/>
|
||||
<path fill="#64666a" fill-rule="evenodd" d="M343 250c0 12-9 21-20 21H145c-11 0-20-9-20-21s9-21 20-21h178c11 0 20 9 20 21z" clip-rule="evenodd"/>
|
||||
<path fill="#64666a" fill-rule="evenodd" d="M309 269c10-11 10-27 0-38l-68-70c-8-8-8-22 0-30s21-8 29 0l97 100c11 11 11 27 0 38l-97 100c-8 8-21 8-29 0s-8-22 0-30z" clip-rule="evenodd"/>
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M38.75 20a18.75 18.75 0 10-37.5 0 18.75 18.75 0 0037.5 0zM20 40a20 20 0 100-40 20 20 0 000 40z" />
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M27.46 20c0 .93-.73 1.68-1.64 1.68H11.64c-.9 0-1.64-.75-1.64-1.68 0-.93.73-1.68 1.64-1.68h14.18c.9 0 1.64.75 1.64 1.68z"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.74 21.51c.81-.83.81-2.19 0-3.02l-5.5-5.62a1.71 1.71 0 010-2.38 1.61 1.61 0 012.32 0l7.83 8c.81.83.81 2.19 0 3.02l-7.83 8c-.64.65-1.67.65-2.32 0a1.71 1.71 0 010-2.38l5.5-5.62z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 542 B After Width: | Height: | Size: 628 B |
|
@ -1,3 +1,3 @@
|
|||
<svg height="500" viewBox="0 0 500 500.00001" width="500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="15.5" y="78" width="469" height="344" rx="50"/>
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M37.5 30V10A2.5 2.5 0 0035 7.5H5A2.5 2.5 0 002.5 10v20A2.5 2.5 0 005 32.5h30a2.5 2.5 0 002.5-2.5zM40 10a5 5 0 00-5-5H5a5 5 0 00-5 5v20a5 5 0 005 5h30a5 5 0 005-5V10z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 291 B |
|
@ -1,3 +1,3 @@
|
|||
<svg height="500" viewBox="0 0 500 500.00001" width="500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect x="78" y="15.5" width="344" height="469" rx="50"/>
|
||||
<svg width="40" height="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M30 2.5H10A2.5 2.5 0 007.5 5v30a2.5 2.5 0 002.5 2.5h20a2.5 2.5 0 002.5-2.5V5A2.5 2.5 0 0030 2.5zM10 0a5 5 0 00-5 5v30a5 5 0 005 5h20a5 5 0 005-5V5a5 5 0 00-5-5H10z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 172 B After Width: | Height: | Size: 289 B |
|
@ -96,6 +96,11 @@
|
|||
position: relative;
|
||||
align-items: center;
|
||||
|
||||
.color-text {
|
||||
width: 3rem;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.attributes-color-display {
|
||||
display: flex;
|
||||
}
|
||||
|
|
|
@ -571,14 +571,13 @@
|
|||
svg {
|
||||
cursor: pointer;
|
||||
height: 20px;
|
||||
stroke: $color-gray-40;
|
||||
stroke-width: 30px;
|
||||
fill: $color-gray-40;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
svg {
|
||||
stroke: $color-gray-10;
|
||||
fill: $color-gray-10;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.util.router :as rt]
|
||||
[app.common.uuid :as uuid]))
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.pages-helpers :as cph]))
|
||||
|
||||
;; --- Specs
|
||||
|
||||
|
@ -242,20 +243,17 @@
|
|||
frames (get-in state [:viewer-data :frames])
|
||||
share-token (get-in state [:viewer-data :share-token])
|
||||
index (d/index-of-pred frames #(= (:id %) frame-id))]
|
||||
(rx/of (rt/nav :viewer {:page-id page-id :file-id file-id} {:token share-token
|
||||
(rx/of (rt/nav :viewer
|
||||
{:page-id page-id
|
||||
:file-id file-id}
|
||||
{:token share-token
|
||||
:index index}))))))
|
||||
|
||||
;; --- Shortcuts
|
||||
|
||||
(def shortcuts
|
||||
{"+" #(st/emit! increase-zoom)
|
||||
"-" #(st/emit! decrease-zoom)
|
||||
"shift+0" #(st/emit! zoom-to-50)
|
||||
"shift+1" #(st/emit! reset-zoom)
|
||||
"shift+2" #(st/emit! zoom-to-200)
|
||||
"left" #(st/emit! select-prev-frame)
|
||||
"right" #(st/emit! select-next-frame)})
|
||||
|
||||
(defn set-current-frame [frame-id]
|
||||
(ptk/reify ::current-frame
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:viewer-data :current-frame-id] frame-id))))
|
||||
|
||||
(defn deselect-all []
|
||||
(ptk/reify ::deselect-all
|
||||
|
@ -264,17 +262,49 @@
|
|||
(assoc-in state [:viewer-local :selected] #{}))))
|
||||
|
||||
(defn select-shape
|
||||
([id] (select-shape id false))
|
||||
([id toggle?]
|
||||
([id]
|
||||
(ptk/reify ::select-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:viewer-local :selected] #{id}))))))
|
||||
|
||||
;; TODO
|
||||
(defn collapse-all []
|
||||
(ptk/reify ::collapse-all))
|
||||
(defn toggle-selection
|
||||
[id]
|
||||
(ptk/reify ::toggle-selection
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [selected (get-in state [:viewer-local :selected])]
|
||||
(cond-> state
|
||||
(not (selected id)) (update-in [:viewer-local :selected] conj id)
|
||||
(selected id) (update-in [:viewer-local :selected] disj id))))))
|
||||
|
||||
(defn shift-select-to
|
||||
[id]
|
||||
(ptk/reify ::shift-select-to
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [objects (get-in state [:viewer-data :objects])
|
||||
selection (-> state
|
||||
(get-in [:viewer-local :selected] #{})
|
||||
(conj id))]
|
||||
(-> state
|
||||
(assoc-in [:viewer-local :selected]
|
||||
(cph/expand-region-selection objects selection)))))))
|
||||
|
||||
(defn select-all
|
||||
[]
|
||||
(ptk/reify ::shift-select-to
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [objects (get-in state [:viewer-data :objects])
|
||||
frame-id (get-in state [:viewer-data :current-frame-id])
|
||||
selection (->> objects
|
||||
(filter #(= (:frame-id (second %)) frame-id))
|
||||
(map first)
|
||||
(into #{frame-id}))]
|
||||
(-> state
|
||||
(assoc-in [:viewer-local :selected] selection))))))
|
||||
|
||||
(defn toggle-collapse [id]
|
||||
(ptk/reify ::toggle-collapse
|
||||
|
@ -288,3 +318,17 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:viewer-local :hover] (if hover? conj disj) id))))
|
||||
|
||||
|
||||
;; --- Shortcuts
|
||||
|
||||
(def shortcuts
|
||||
{"+" #(st/emit! increase-zoom)
|
||||
"-" #(st/emit! decrease-zoom)
|
||||
"ctrl+a" #(st/emit! (select-all))
|
||||
"shift+0" #(st/emit! zoom-to-50)
|
||||
"shift+1" #(st/emit! reset-zoom)
|
||||
"shift+2" #(st/emit! zoom-to-200)
|
||||
"left" #(st/emit! select-prev-frame)
|
||||
"right" #(st/emit! select-next-frame)})
|
||||
|
||||
|
|
|
@ -74,8 +74,12 @@
|
|||
(logjs "state" @state))
|
||||
|
||||
(defn ^:export dump-objects []
|
||||
(let [page-id (get @state :current-page-id)]
|
||||
(logjs "state" (get-in @state [:workspace-data :pages-index page-id :objects]))))
|
||||
(let [page-id (get @state :current-page-id)
|
||||
objects (get-in @state [:workspace-data :pages-index page-id :objects])]
|
||||
(logjs "index" (->> (cph/indexed-shapes objects)
|
||||
(d/mapm (fn [k id] (get objects id)))
|
||||
(map (fn [[idx obj]] (str idx " - " (:name obj))))))
|
||||
(logjs "state" objects)))
|
||||
|
||||
(defn ^:export dump-object [name]
|
||||
(let [page-id (get @state :current-page-id)]
|
||||
|
|
|
@ -150,10 +150,9 @@
|
|||
(mf/defc debug-icons-preview
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
[:section.debug-icons-preview {:style {:background-color "black"}}
|
||||
[:section.debug-icons-preview
|
||||
(for [[key val] (sort-by first (ns-publics 'app.main.ui.icons))]
|
||||
(when (not= key 'debug-icons-preview)
|
||||
[:div.icon-item {:key key
|
||||
:style {:fill "white"}}
|
||||
[:div.icon-item {:key key}
|
||||
(deref val)
|
||||
[:span (pr-str key)]]))])
|
||||
|
|
|
@ -26,10 +26,9 @@
|
|||
[app.main.ui.keyboard :as kbd]
|
||||
[app.main.ui.viewer.header :refer [header]]
|
||||
[app.main.ui.viewer.thumbnails :refer [thumbnails-panel]]
|
||||
|
||||
[app.main.ui.viewer.handoff.render :refer [render-frame-svg]]
|
||||
[app.main.ui.viewer.handoff.layers-sidebar :refer [layers-sidebar]]
|
||||
[app.main.ui.viewer.handoff.attributes-sidebar :refer [attributes-sidebar]])
|
||||
[app.main.ui.viewer.handoff.left-sidebar :refer [left-sidebar]]
|
||||
[app.main.ui.viewer.handoff.right-sidebar :refer [right-sidebar]])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(defn handle-select-frame [frame]
|
||||
|
@ -47,7 +46,8 @@
|
|||
(mf/use-effect
|
||||
(mf/deps index)
|
||||
(fn []
|
||||
(st/emit! (dv/select-shape (:id frame)))))
|
||||
(st/emit! (dv/set-current-frame (:id frame))
|
||||
(dv/select-shape (:id frame)))))
|
||||
|
||||
[:section.viewer-preview
|
||||
(cond
|
||||
|
@ -61,12 +61,12 @@
|
|||
|
||||
:else
|
||||
[:*
|
||||
[:& layers-sidebar {:frame frame}]
|
||||
[:& left-sidebar {:frame frame}]
|
||||
[:div.handoff-svg-wrapper {:on-click (handle-select-frame frame)}
|
||||
[:& render-frame-svg {:frame-id (:id frame)
|
||||
:zoom (:zoom local)
|
||||
:objects objects}]]
|
||||
[:& attributes-sidebar {:frame frame}]])]))
|
||||
[:& right-sidebar {:frame frame}]])]))
|
||||
|
||||
(mf/defc handoff-content
|
||||
[{:keys [data local index] :as props}]
|
||||
|
|
|
@ -1,440 +0,0 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attrib-panel
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.config :as cfg]
|
||||
[app.util.data :as d]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [locale t]]
|
||||
[app.util.color :as uc]
|
||||
[app.util.text :as ut]
|
||||
[app.common.math :as mth]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]))
|
||||
|
||||
(defn copy-cb [values properties & {:keys [to-prop format] :or {to-prop {}}}]
|
||||
(fn [event]
|
||||
(let [
|
||||
;; We allow the :format and :to-prop to be a map for different properties
|
||||
;; or just a value for a single property. This code transform a single
|
||||
;; property to a uniform one
|
||||
properties (if-not (coll? properties) [properties] properties)
|
||||
|
||||
format (if (not (map? format))
|
||||
(into {} (map #(vector % format) properties))
|
||||
format)
|
||||
|
||||
to-prop (if (not (map? to-prop))
|
||||
(into {} (map #(vector % to-prop) properties))
|
||||
to-prop)
|
||||
|
||||
default-format (fn [value] (str (mth/precision value 2) "px"))
|
||||
format-property (fn [prop]
|
||||
(let [css-prop (or (prop to-prop) (name prop))]
|
||||
(str/fmt " %s: %s;" css-prop ((or (prop format) default-format) (prop values) values))))
|
||||
|
||||
text-props (->> properties
|
||||
(remove #(let [value (get values %)]
|
||||
(or (nil? value) (= value 0))))
|
||||
(map format-property)
|
||||
(str/join "\n"))
|
||||
|
||||
result (str/fmt "{\n%s\n}" text-props)]
|
||||
|
||||
(wapi/write-to-clipboard result))))
|
||||
|
||||
|
||||
(mf/defc color-row [{:keys [color format on-copy on-change-format]}]
|
||||
(let [locale (mf/deref locale)]
|
||||
[:div.attributes-color-row
|
||||
[:& color-bullet {:color color}]
|
||||
|
||||
(if (:gradient color)
|
||||
[:& color-name {:color color}]
|
||||
(case format
|
||||
:rgba (let [[r g b a] (->> (uc/hex->rgba (:color color) (:opacity color)) (map #(mth/precision % 2)))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
:hsla (let [[h s l a] (->> (uc/hex->hsla (:color color) (:opacity color)) (map #(mth/precision % 2)))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" h s l a)])
|
||||
[:*
|
||||
[:& color-name {:color color}]
|
||||
(when-not (:gradient color) [:div (str (* 100 (:opacity color)) "%")])]))
|
||||
|
||||
(when-not (and on-change-format (:gradient color))
|
||||
[:select {:on-change #(-> (dom/get-target-val %) keyword on-change-format)}
|
||||
[:option {:value "hex"}
|
||||
(t locale "handoff.attributes.color.hex")]
|
||||
|
||||
[:option {:value "rgba"}
|
||||
(t locale "handoff.attributes.color.rgba")]
|
||||
|
||||
[:option {:value "hsla"}
|
||||
(t locale "handoff.attributes.color.hsla")]])
|
||||
|
||||
(when on-copy
|
||||
[:button.attributes-copy-button {:on-click on-copy} i/copy])]))
|
||||
|
||||
(mf/defc layout-panel
|
||||
[{:keys [shape locale]}]
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.layout")]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape
|
||||
[:width :height :x :y :rotation]
|
||||
:to-prop {:x "left" :y "top" :rotation "transform"}
|
||||
:format {:rotation #(str/fmt "rotate(%sdeg)" %)})}
|
||||
i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.width")]
|
||||
[:div.attributes-value (mth/precision (:width shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :width)}
|
||||
i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.height")]
|
||||
[:div.attributes-value (mth/precision (:height shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :height)}
|
||||
i/copy]]
|
||||
|
||||
(when (not= (:x shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.left")]
|
||||
[:div.attributes-value (mth/precision (:x shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :x :to-prop "left")}
|
||||
i/copy]])
|
||||
|
||||
(when (not= (:y shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.top")]
|
||||
[:div.attributes-value (mth/precision (:y shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :y :to-prop "top")}
|
||||
i/copy]])
|
||||
|
||||
(when (not= (:rotation shape 0) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.rotation")]
|
||||
[:div.attributes-value (mth/precision (:rotation shape) 2) "deg"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape
|
||||
:rotation
|
||||
:to-prop "transform"
|
||||
:format #(str/fmt "rotate(%sdeg)" %))}
|
||||
i/copy]])])
|
||||
|
||||
(mf/defc fill-panel
|
||||
[{:keys [shape locale]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color {:color (:fill-color shape)
|
||||
:opacity (:fill-opacity shape)
|
||||
:gradient (:fill-color-gradient shape)
|
||||
:id (:fill-ref-id shape)
|
||||
:file-id (:fill-ref-file-id shape)}
|
||||
|
||||
handle-copy (copy-cb shape
|
||||
[:fill-color :fill-color-gradient]
|
||||
:to-prop "background"
|
||||
:format #(uc/color->background color))]
|
||||
|
||||
(when (or (:color color) (:gradient color))
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.fill")]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy}
|
||||
i/copy]]
|
||||
|
||||
[:& color-row {:color color
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:on-copy handle-copy}]])))
|
||||
|
||||
(mf/defc stroke-panel
|
||||
[{:keys [shape locale]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color {:color (:stroke-color shape)
|
||||
:opacity (:stroke-opacity shape)
|
||||
:gradient (:stroke-color-gradient shape)
|
||||
:id (:stroke-color-ref-id shape)
|
||||
:file-id (:stroke-color-file-id shape)}
|
||||
|
||||
handle-copy-stroke (copy-cb shape
|
||||
:stroke-style
|
||||
:to-prop "border"
|
||||
:format #(let [width (:stroke-width %2)
|
||||
style (name (:stroke-style %2))
|
||||
color (uc/color->background color)]
|
||||
(str/format "%spx %s %s" width style color)))]
|
||||
|
||||
(when (and (:stroke-style shape) (not= (:stroke-style shape) :none))
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.stroke")]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy-stroke} i/copy]]
|
||||
|
||||
[:& color-row {:color color
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:on-copy (copy-cb shape
|
||||
:stroke-color
|
||||
:to-prop "border-color"
|
||||
:format #(uc/color->background color))}]
|
||||
|
||||
[:div.attributes-stroke-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.stroke.width")]
|
||||
[:div.attributes-value (:stroke-width shape) "px"]
|
||||
[:div.attributes-value (->> shape :stroke-style name (str "handoff.attributes.stroke.style.") (t locale))]
|
||||
[:div.attributes-label (->> shape :stroke-alignment name (str "handoff.attributes.stroke.alignment.") (t locale))]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy-stroke} i/copy]]])))
|
||||
|
||||
(defn shadow->css [shadow]
|
||||
(let [{:keys [style offset-x offset-y blur spread]} shadow
|
||||
css-color (uc/color->background (:color shadow))]
|
||||
(str
|
||||
(if (= style :inner-shadow) "inset " "")
|
||||
(str/fmt "%spx %spx %spx %spx %s" offset-x offset-y blur spread css-color))))
|
||||
|
||||
(mf/defc shadow-block [{:keys [shape locale shadow]}]
|
||||
(let [color-format (mf/use-state :hex)]
|
||||
[:div.attributes-shadow-block
|
||||
[:div.attributes-shadow-row
|
||||
[:div.attributes-label (->> shadow :style name (str "handoff.attributes.shadow.style.") (t locale))]
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-x")]
|
||||
[:div.attributes-value (str (:offset-x shadow))]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-y")]
|
||||
[:div.attributes-value (str (:offset-y shadow))]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.blur")]
|
||||
[:div.attributes-value (str (:blur shadow))]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.spread")]
|
||||
[:div.attributes-value (str (:spread shadow))]]
|
||||
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shadow
|
||||
:style
|
||||
:to-prop "box-shadow"
|
||||
:format #(shadow->css shadow))}
|
||||
i/copy]]
|
||||
[:& color-row {:color (:color shadow)
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)}]]))
|
||||
|
||||
(mf/defc shadow-panel [{:keys [shape locale]}]
|
||||
(when (seq (:shadow shape))
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.shadow")]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape
|
||||
:shadow
|
||||
:to-prop "box-shadow"
|
||||
:format #(str/join ", " (map shadow->css (:shadow shape))))}
|
||||
i/copy]]
|
||||
|
||||
(for [shadow (:shadow shape)]
|
||||
[:& shadow-block {:shape shape
|
||||
:locale locale
|
||||
:shadow shadow}])]))
|
||||
|
||||
(mf/defc blur-panel [{:keys [shape locale]}]
|
||||
(let [handle-copy
|
||||
(copy-cb shape
|
||||
:blur
|
||||
:to-prop "filter"
|
||||
:format #(str/fmt "blur(%spx)" %))]
|
||||
(when (:blur shape)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.blur")]
|
||||
[:button.attributes-copy-button {:on-click handle-copy} i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.blur.value")]
|
||||
[:div.attributes-value (-> shape :blur :value) "px"]
|
||||
[:button.attributes-copy-button {:on-click handle-copy} i/copy]]])))
|
||||
|
||||
(mf/defc image-panel [{:keys [shape locale]}]
|
||||
[:div.attributes-block
|
||||
[:div.attributes-image-row
|
||||
[:div.attributes-image
|
||||
[:img {:src (cfg/resolve-media-path (-> shape :metadata :path))}]]]
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.image.width")]
|
||||
[:div.attributes-value (-> shape :metadata :width) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb shape :width)} i/copy]]
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.image.height")]
|
||||
[:div.attributes-value (-> shape :metadata :height) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb shape :height)} i/copy]]
|
||||
(let [filename (last (str/split (-> shape :metadata :path) "/"))]
|
||||
[:a.download-button {:target "_blank"
|
||||
:download filename
|
||||
:href (cfg/resolve-media-path (-> shape :metadata :path))}
|
||||
(t locale "handoff.attributes.image.download")])])
|
||||
|
||||
|
||||
(mf/defc text-block [{:keys [shape locale text style full-style]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color {:color (:fill-color style)
|
||||
:opacity (:fill-opacity style)
|
||||
:gradient (:fill-color-gradient style)
|
||||
:id (:fill-color-ref-id style)
|
||||
:file-id (:fill-color-ref-file-id style)}
|
||||
properties [:fill-color
|
||||
:fill-color-gradient
|
||||
:font-family
|
||||
:font-style
|
||||
:font-size
|
||||
:line-height
|
||||
:letter-spacing
|
||||
:text-decoration
|
||||
:text-transform]
|
||||
format {:font-family identity
|
||||
:font-style identity
|
||||
:font-size #(str % "px")
|
||||
:line-height #(str % "px")
|
||||
:letter-spacing #(str % "px")
|
||||
:text-decoration name
|
||||
:text-transform name
|
||||
:fill-color #(uc/color->background color)
|
||||
:fill-color-gradient #(uc/color->background color)}
|
||||
to-prop {:fill-color "color"
|
||||
:fill-color-gradient "color"}]
|
||||
[:div.attributes-text-block
|
||||
[:div.attributes-typography-row
|
||||
[:div.typography-sample
|
||||
{:style {:font-family (:font-family full-style)
|
||||
:font-weight (:font-weight full-style)
|
||||
:font-style (:font-style full-style)}}
|
||||
(t locale "workspace.assets.typography.sample")]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb style properties :to-prop to-prop :format format)} i/copy]]
|
||||
|
||||
[:div.attributes-content-row
|
||||
[:pre.attributes-content (str/trim text)]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click #(wapi/write-to-clipboard (str/trim text))}
|
||||
i/copy]]
|
||||
|
||||
(when (or (:fill-color style) (:fill-color-gradient style))
|
||||
(let [color {:color (:fill-color style)
|
||||
:opacity (:fill-opacity style)
|
||||
:gradient (:fill-color-gradient style)
|
||||
:id (:fill-ref-id style)
|
||||
:file-id (:fill-ref-file-id style)}]
|
||||
[:& color-row {:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:color color
|
||||
:on-copy (copy-cb style [:fill-color :fill-color-gradient] :to-prop to-prop :format format)}]))
|
||||
|
||||
(when (:font-id style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-family")]
|
||||
[:div.attributes-value (-> style :font-id fonts/get-font-data :name)]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-family :format identity)} i/copy]])
|
||||
|
||||
(when (:font-style style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-style")]
|
||||
[:div.attributes-value (str (:font-style style))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-style :format identity)} i/copy]])
|
||||
|
||||
(when (:font-size style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-size")]
|
||||
[:div.attributes-value (str (:font-size style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-size :format #(str % "px"))} i/copy]])
|
||||
|
||||
(when (:line-height style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.line-height")]
|
||||
[:div.attributes-value (str (:line-height style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :line-height :format #(str % "px"))} i/copy]])
|
||||
|
||||
(when (:letter-spacing style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.letter-spacing")]
|
||||
[:div.attributes-value (str (:letter-spacing style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :letter-spacing :format #(str % "px"))} i/copy]])
|
||||
|
||||
(when (:text-decoration style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-decoration")]
|
||||
[:div.attributes-value (->> style :text-decoration (str "handoff.attributes.typography.text-decoration.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :text-decoration :format name)} i/copy]])
|
||||
|
||||
(when (:text-transform style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-transform")]
|
||||
[:div.attributes-value (->> style :text-transform (str "handoff.attributes.typography.text-transform.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :text-transform :format name)} i/copy]])]))
|
||||
|
||||
(mf/defc typography-panel [{:keys [shape locale]}]
|
||||
(let [font (ut/search-text-attrs (:content shape)
|
||||
(keys ut/default-text-attrs))
|
||||
|
||||
style-text-blocks (->> (keys ut/default-text-attrs)
|
||||
(ut/parse-style-text-blocks (:content shape))
|
||||
(remove (fn [[style text]] (str/empty? (str/trim text))))
|
||||
(mapv (fn [[style text]] (vector (merge ut/default-text-attrs style) text))))
|
||||
|
||||
font (merge ut/default-text-attrs font)]
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.typography")]]
|
||||
|
||||
(for [[idx [full-style text]] (map-indexed vector style-text-blocks)]
|
||||
(let [previus-style (first (nth style-text-blocks (dec idx) nil))
|
||||
style (d/remove-equal-values full-style previus-style)
|
||||
|
||||
;; If the color is set we need to add opacity otherwise the display will not work
|
||||
style (cond-> style
|
||||
(:fill-color style)
|
||||
(assoc :fill-opacity (:fill-opacity full-style)))]
|
||||
[:& text-block {:shape shape
|
||||
:locale locale
|
||||
:full-style full-style
|
||||
:style style
|
||||
:text text}]))]))
|
||||
|
||||
(mf/defc attrib-panel [{:keys [shape frame options]}]
|
||||
(let [locale (mf/deref locale)]
|
||||
[:div.element-options
|
||||
(for [option options]
|
||||
[:>
|
||||
(case option
|
||||
:layout layout-panel
|
||||
:fill fill-panel
|
||||
:stroke stroke-panel
|
||||
:shadow shadow-panel
|
||||
:blur blur-panel
|
||||
:image image-panel
|
||||
:typography typography-panel)
|
||||
{:shape (gsh/translate-to-frame shape frame)
|
||||
:frame frame
|
||||
:locale locale}])]))
|
43
frontend/src/app/main/ui/viewer/handoff/attributes.cljs
Normal file
43
frontend/src/app/main/ui/viewer/handoff/attributes.cljs
Normal file
|
@ -0,0 +1,43 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[app.util.i18n :as i18n]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.viewer.handoff.attributes.layout :refer [layout-panel]]
|
||||
[app.main.ui.viewer.handoff.attributes.fill :refer [fill-panel]]
|
||||
[app.main.ui.viewer.handoff.attributes.stroke :refer [stroke-panel]]
|
||||
[app.main.ui.viewer.handoff.attributes.shadow :refer [shadow-panel]]
|
||||
[app.main.ui.viewer.handoff.attributes.blur :refer [blur-panel]]
|
||||
[app.main.ui.viewer.handoff.attributes.image :refer [image-panel]]
|
||||
[app.main.ui.viewer.handoff.attributes.text :refer [text-panel]]))
|
||||
|
||||
(mf/defc attributes
|
||||
[{:keys [shapes frame options]}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
shapes (->> shapes
|
||||
(map #(gsh/translate-to-frame % frame)))
|
||||
|
||||
shape (first shapes)]
|
||||
[:div.element-options
|
||||
(for [option options]
|
||||
[:> (case option
|
||||
:layout layout-panel
|
||||
:fill fill-panel
|
||||
:stroke stroke-panel
|
||||
:shadow shadow-panel
|
||||
:blur blur-panel
|
||||
:image image-panel
|
||||
:text text-panel)
|
||||
{:shapes shapes
|
||||
:frame frame
|
||||
:locale locale}])]))
|
||||
|
41
frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs
Normal file
41
frontend/src/app/main/ui/viewer/handoff/attributes/blur.cljs
Normal file
|
@ -0,0 +1,41 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.blur
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb]]))
|
||||
|
||||
(defn has-blur? [shape]
|
||||
(:blur shape))
|
||||
|
||||
(defn copy-blur [shape]
|
||||
(copy-cb shape
|
||||
:blur
|
||||
:to-prop "filter"
|
||||
:format #(str/fmt "blur(%spx)" (:value %))))
|
||||
|
||||
(mf/defc blur-panel [{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-blur?))
|
||||
handle-copy (when (= (count shapes) 1) (copy-blur (first shapes)))]
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.blur")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button {:on-click handle-copy} i/copy])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.blur.value")]
|
||||
[:div.attributes-value (-> shape :blur :value) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-blur shape)} i/copy]])])))
|
|
@ -0,0 +1,81 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.common
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [t] :as i18n]
|
||||
[app.util.color :as uc]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]))
|
||||
|
||||
(defn copy-cb [values properties & {:keys [to-prop format] :or {to-prop {}}}]
|
||||
(fn [event]
|
||||
(let [
|
||||
;; We allow the :format and :to-prop to be a map for different properties
|
||||
;; or just a value for a single property. This code transform a single
|
||||
;; property to a uniform one
|
||||
properties (if-not (coll? properties) [properties] properties)
|
||||
|
||||
format (if (not (map? format))
|
||||
(into {} (map #(vector % format) properties))
|
||||
format)
|
||||
|
||||
to-prop (if (not (map? to-prop))
|
||||
(into {} (map #(vector % to-prop) properties))
|
||||
to-prop)
|
||||
|
||||
default-format (fn [value] (str (mth/precision value 2) "px"))
|
||||
format-property (fn [prop]
|
||||
(let [css-prop (or (prop to-prop) (name prop))]
|
||||
(str/fmt " %s: %s;" css-prop ((or (prop format) default-format) (prop values) values))))
|
||||
|
||||
text-props (->> properties
|
||||
(remove #(let [value (get values %)]
|
||||
(or (nil? value) (= value 0))))
|
||||
(map format-property)
|
||||
(str/join "\n"))
|
||||
|
||||
result (str/fmt "{\n%s\n}" text-props)]
|
||||
|
||||
(wapi/write-to-clipboard result))))
|
||||
|
||||
(mf/defc color-row [{:keys [color format on-copy on-change-format]}]
|
||||
(let [locale (mf/deref i18n/locale)]
|
||||
[:div.attributes-color-row
|
||||
[:& color-bullet {:color color}]
|
||||
|
||||
(if (:gradient color)
|
||||
[:& color-name {:color color}]
|
||||
(case format
|
||||
:rgba (let [[r g b a] (->> (uc/hex->rgba (:color color) (:opacity color)) (map #(mth/precision % 2)))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" r g b a)])
|
||||
:hsla (let [[h s l a] (->> (uc/hex->hsla (:color color) (:opacity color)) (map #(mth/precision % 2)))]
|
||||
[:div (str/fmt "%s, %s, %s, %s" h s l a)])
|
||||
[:*
|
||||
[:& color-name {:color color}]
|
||||
(when-not (:gradient color) [:div (str (* 100 (:opacity color)) "%")])]))
|
||||
|
||||
(when-not (and on-change-format (:gradient color))
|
||||
[:select {:on-change #(-> (dom/get-target-val %) keyword on-change-format)}
|
||||
[:option {:value "hex"}
|
||||
(t locale "handoff.attributes.color.hex")]
|
||||
|
||||
[:option {:value "rgba"}
|
||||
(t locale "handoff.attributes.color.rgba")]
|
||||
|
||||
[:option {:value "hsla"}
|
||||
(t locale "handoff.attributes.color.hsla")]])
|
||||
|
||||
(when on-copy
|
||||
[:button.attributes-copy-button {:on-click on-copy} i/copy])]))
|
67
frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs
Normal file
67
frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs
Normal file
|
@ -0,0 +1,67 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.fill
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.util.color :as uc]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
|
||||
(def fill-attributes [:fill-color :fill-color-gradient])
|
||||
|
||||
(defn shape->color [shape]
|
||||
{:color (:fill-color shape)
|
||||
:opacity (:fill-opacity shape)
|
||||
:gradient (:fill-color-gradient shape)
|
||||
:id (:fill-ref-id shape)
|
||||
:file-id (:fill-ref-file-id shape)})
|
||||
|
||||
(defn has-color? [shape]
|
||||
(and
|
||||
(not (contains? #{:image :text :group} (:type shape)))
|
||||
(or (:fill-color shape)
|
||||
(:fill-color-gradient shape))))
|
||||
|
||||
(mf/defc fill-block [{:keys [shape locale]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color (shape->color shape)
|
||||
handle-copy (copy-cb shape
|
||||
fill-attributes
|
||||
:to-prop "background"
|
||||
:format #(uc/color->background color))]
|
||||
|
||||
[:& color-row {:color color
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:on-copy handle-copy}]))
|
||||
|
||||
(mf/defc fill-panel
|
||||
[{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-color?))
|
||||
handle-copy (when (= (count shapes) 1)
|
||||
(copy-cb (first shapes)
|
||||
fill-attributes
|
||||
:to-prop "background"
|
||||
:format #(-> shapes first shape->color uc/color->background)))]
|
||||
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.fill")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy}
|
||||
i/copy])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:& fill-block {:key (str "fill-block-" (:id shape))
|
||||
:shape shape
|
||||
:locale locale}])])))
|
|
@ -0,0 +1,44 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.image
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.config :as cfg]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb]]))
|
||||
|
||||
(defn has-image? [shape]
|
||||
(and (= (:type shape) :image)))
|
||||
|
||||
(mf/defc image-panel [{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-image?))]
|
||||
(for [shape shapes]
|
||||
[:div.attributes-block {:key (str "image-" (:id shape))}
|
||||
[:div.attributes-image-row
|
||||
[:div.attributes-image
|
||||
[:img {:src (cfg/resolve-media-path (-> shape :metadata :path))}]]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.image.width")]
|
||||
[:div.attributes-value (-> shape :metadata :width) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb shape :width)} i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.image.height")]
|
||||
[:div.attributes-value (-> shape :metadata :height) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb shape :height)} i/copy]]
|
||||
|
||||
(let [filename (last (str/split (-> shape :metadata :path) "/"))]
|
||||
[:a.download-button {:target "_blank"
|
||||
:download filename
|
||||
:href (cfg/resolve-media-path (-> shape :metadata :path))}
|
||||
(t locale "handoff.attributes.image.download")])])))
|
|
@ -10,100 +10,75 @@
|
|||
(ns app.main.ui.viewer.handoff.attributes.layout
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.components.color-bullet :refer [color-bullet color-name]]))
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb]]))
|
||||
|
||||
(defn copy-layout [shape]
|
||||
(copy-cb shape
|
||||
[:width :height :x :y :rotation]
|
||||
:to-prop {:x "left" :y "top" :rotation "transform"}
|
||||
:format {:rotation #(str/fmt "rotate(%sdeg)" %)}))
|
||||
|
||||
(mf/defc layout-panel [{:keys [shapes]}]
|
||||
(mf/defc layout-block
|
||||
[{:keys [shape locale]}]
|
||||
[:*
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.width")]
|
||||
[:div.attributes-value (mth/precision (:width shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :width)}
|
||||
i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.height")]
|
||||
[:div.attributes-value (mth/precision (:height shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :height)}
|
||||
i/copy]]
|
||||
|
||||
(when (not= (:x shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.left")]
|
||||
[:div.attributes-value (mth/precision (:x shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :x :to-prop "left")}
|
||||
i/copy]])
|
||||
|
||||
(when (not= (:y shape) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.top")]
|
||||
[:div.attributes-value (mth/precision (:y shape) 2) "px"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape :y :to-prop "top")}
|
||||
i/copy]])
|
||||
|
||||
(when (not= (:rotation shape 0) 0)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.layout.rotation")]
|
||||
[:div.attributes-value (mth/precision (:rotation shape) 2) "deg"]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shape
|
||||
:rotation
|
||||
:to-prop "transform"
|
||||
:format #(str/fmt "rotate(%sdeg)" %))}
|
||||
i/copy]])])
|
||||
|
||||
|
||||
(mf/defc layout-panel
|
||||
[{:keys [shapes locale]}]
|
||||
(let [handle-copy (when (= (count shapes) 1)
|
||||
(copy-layout (first shapes)))]
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text "Layout"]
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.layout")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy}
|
||||
i/copy])]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label "Width"]
|
||||
[:div.attributes-value "100px"]
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label "Height"]
|
||||
[:div.attributes-value "100px"]
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label "Top"]
|
||||
[:div.attributes-value "100px"]
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label "Left"]
|
||||
[:div.attributes-value "100px"]
|
||||
[:button.attributes-copy-button i/copy]]]
|
||||
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text "Fill"]
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
|
||||
[:div.attributes-shadow-row
|
||||
[:div.attributes-label "Drop"]
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label "X"]
|
||||
[:div.attributes-value "4"]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label "Y"]
|
||||
[:div.attributes-value "4"]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label "B"]
|
||||
[:div.attributes-value "0"]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label "B"]
|
||||
[:div.attributes-value "0"]]
|
||||
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
|
||||
[:div.attributes-color-row
|
||||
[:& color-bullet {:color {:color "#000000" :opacity 0.5}}]
|
||||
|
||||
[:*
|
||||
[:div "#000000"]
|
||||
[:div "100%"]]
|
||||
|
||||
[:select
|
||||
[:option "Hex"]
|
||||
[:option "RGBA"]
|
||||
[:option "HSLA"]]
|
||||
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
|
||||
[:div.attributes-stroke-row
|
||||
[:div.attributes-label "Width"]
|
||||
[:div.attributes-value "1px"]
|
||||
[:div.attributes-value "Solid"]
|
||||
[:div.attributes-label "Center"]
|
||||
[:button.attributes-copy-button i/copy]]]
|
||||
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text "Content"]
|
||||
[:button.attributes-copy-button i/copy]]
|
||||
|
||||
[:div.attributes-content-row
|
||||
[:div.attributes-content
|
||||
"Hi, how are you"]
|
||||
[:button.attributes-copy-button i/copy]]]
|
||||
|
||||
[:div.attributes-block
|
||||
[:div.attributes-image-row
|
||||
[:div.attributes-image
|
||||
#_[:img {:src "https://www.publico.es/tremending/wp-content/uploads/2019/05/Cxagv.jpg"}]
|
||||
#_[:img {:src "https://i.blogs.es/3861b2/grumpy-cat/1366_2000.png"}]
|
||||
[:img {:src "https://abs.twimg.com/favicons/twitter.ico"}]
|
||||
]]
|
||||
[:button.download-button "Dowload source image"]]
|
||||
|
||||
])
|
||||
(for [shape shapes]
|
||||
[:& layout-block {:shape shape
|
||||
:locale locale}])]))
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.shadow
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.util.color :as uc]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
|
||||
(defn has-shadow? [shape]
|
||||
(:shadow shape))
|
||||
|
||||
(defn shadow->css [shadow]
|
||||
(let [{:keys [style offset-x offset-y blur spread]} shadow
|
||||
css-color (uc/color->background (:color shadow))]
|
||||
(str
|
||||
(if (= style :inner-shadow) "inset " "")
|
||||
(str/fmt "%spx %spx %spx %spx %s" offset-x offset-y blur spread css-color))))
|
||||
|
||||
|
||||
(mf/defc shadow-block [{:keys [shape locale shadow]}]
|
||||
(let [color-format (mf/use-state :hex)]
|
||||
[:div.attributes-shadow-block
|
||||
[:div.attributes-shadow-row
|
||||
[:div.attributes-label (->> shadow :style name (str "handoff.attributes.shadow.style.") (t locale))]
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-x")]
|
||||
[:div.attributes-value (str (:offset-x shadow))]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.offset-y")]
|
||||
[:div.attributes-value (str (:offset-y shadow))]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.blur")]
|
||||
[:div.attributes-value (str (:blur shadow))]]
|
||||
|
||||
[:div.attributes-shadow
|
||||
[:div.attributes-label (t locale "handoff.attributes.shadow.shorthand.spread")]
|
||||
[:div.attributes-value (str (:spread shadow))]]
|
||||
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb shadow
|
||||
:style
|
||||
:to-prop "box-shadow"
|
||||
:format #(shadow->css shadow))}
|
||||
i/copy]]
|
||||
[:& color-row {:color (:color shadow)
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)}]]))
|
||||
|
||||
(mf/defc shadow-panel [{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-shadow?))
|
||||
handle-copy-shadow (when (= (count shapes) 1)
|
||||
(copy-cb (first shapes)
|
||||
:shadow
|
||||
:to-prop "box-shadow"
|
||||
:format #(str/join ", " (map shadow->css (:shadow (first shapes))))))]
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.shadow")]
|
||||
(when handle-copy-shadow
|
||||
[:button.attributes-copy-button {:on-click handle-copy-shadow} i/copy])]
|
||||
|
||||
(for [shape shapes]
|
||||
(for [shadow (:shadow shape)]
|
||||
[:& shadow-block {:shape shape
|
||||
:locale locale
|
||||
:shadow shadow}]))])))
|
|
@ -0,0 +1,83 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.stroke
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.util.color :as uc]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
|
||||
(defn shape->color [shape]
|
||||
{:color (:stroke-color shape)
|
||||
:opacity (:stroke-opacity shape)
|
||||
:gradient (:stroke-color-gradient shape)
|
||||
:id (:stroke-ref-id shape)
|
||||
:file-id (:stroke-ref-file-id shape)})
|
||||
|
||||
(defn format-stroke [shape]
|
||||
(let [width (:stroke-width shape)
|
||||
style (name (:stroke-style shape))
|
||||
color (-> shape shape->color uc/color->background)]
|
||||
(str/format "%spx %s %s" width style color)))
|
||||
|
||||
(defn has-stroke? [shape]
|
||||
(and (:stroke-style shape)
|
||||
(not= (:stroke-style shape) :none)))
|
||||
|
||||
(mf/defc stroke-block
|
||||
[{:keys [shape locale]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color (shape->color shape)
|
||||
handle-copy-stroke (copy-cb shape
|
||||
:stroke-style
|
||||
:to-prop "border"
|
||||
:format #(format-stroke shape))
|
||||
|
||||
handle-copy-color (copy-cb shape
|
||||
:stroke-color
|
||||
:to-prop "border-color"
|
||||
:format #(uc/color->background color))]
|
||||
|
||||
[:*
|
||||
[:& color-row {:color color
|
||||
:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:on-copy handle-copy-color}]
|
||||
|
||||
[:div.attributes-stroke-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.stroke.width")]
|
||||
[:div.attributes-value (:stroke-width shape) "px"]
|
||||
[:div.attributes-value (->> shape :stroke-style name (str "handoff.attributes.stroke.style.") (t locale))]
|
||||
[:div.attributes-label (->> shape :stroke-alignment name (str "handoff.attributes.stroke.alignment.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click handle-copy-stroke} i/copy]]]))
|
||||
|
||||
(mf/defc stroke-panel
|
||||
[{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-stroke?))
|
||||
handle-copy (when (= (count shapes) 1)
|
||||
(copy-cb (first shapes)
|
||||
:stroke-style
|
||||
:to-prop "border"
|
||||
:format #(format-stroke (first shapes))))]
|
||||
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.stroke")]
|
||||
(when handle-copy
|
||||
[:button.attributes-copy-button
|
||||
{:on-click handle-copy} i/copy])]
|
||||
|
||||
(for [shape shapes]
|
||||
[:& stroke-block {:key (str "stroke-color-" (:id shape))
|
||||
:shape shape
|
||||
:locale locale}])])))
|
164
frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs
Normal file
164
frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs
Normal file
|
@ -0,0 +1,164 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
;; defined by the Mozilla Public License, v. 2.0.
|
||||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes.text
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[app.util.data :as d]
|
||||
[app.util.i18n :refer [t]]
|
||||
[app.util.color :as uc]
|
||||
[app.util.text :as ut]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.webapi :as wapi]
|
||||
[app.main.ui.viewer.handoff.attributes.common :refer [copy-cb color-row]]))
|
||||
|
||||
(defn has-text? [shape]
|
||||
(:content shape))
|
||||
|
||||
(def properties [:fill-color
|
||||
:fill-color-gradient
|
||||
:font-family
|
||||
:font-style
|
||||
:font-size
|
||||
:line-height
|
||||
:letter-spacing
|
||||
:text-decoration
|
||||
:text-transform])
|
||||
|
||||
(defn shape->color [shape]
|
||||
{:color (:fill-color shape)
|
||||
:opacity (:fill-opacity shape)
|
||||
:gradient (:fill-color-gradient shape)
|
||||
:id (:fill-ref-id shape)
|
||||
:file-id (:fill-ref-file-id shape)})
|
||||
|
||||
(defn format-style [color]
|
||||
{:font-family #(str "'" % "'")
|
||||
:font-style #(str "'" % "'")
|
||||
:font-size #(str % "px")
|
||||
:line-height #(str % "px")
|
||||
:letter-spacing #(str % "px")
|
||||
:text-decoration name
|
||||
:text-transform name
|
||||
:fill-color #(uc/color->background color)
|
||||
:fill-color-gradient #(uc/color->background color)})
|
||||
|
||||
(mf/defc typography-block [{:keys [shape locale text style full-style]}]
|
||||
(let [color-format (mf/use-state :hex)
|
||||
color (shape->color style)
|
||||
to-prop {:fill-color "color"
|
||||
:fill-color-gradient "color"}]
|
||||
[:div.attributes-text-block
|
||||
[:div.attributes-typography-row
|
||||
[:div.typography-sample
|
||||
{:style {:font-family (:font-family full-style)
|
||||
:font-weight (:font-weight full-style)
|
||||
:font-style (:font-style full-style)}}
|
||||
(t locale "workspace.assets.typography.sample")]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click (copy-cb style properties
|
||||
:to-prop to-prop
|
||||
:format (format-style color))}
|
||||
i/copy]]
|
||||
|
||||
[:div.attributes-content-row
|
||||
[:pre.attributes-content (str/trim text)]
|
||||
[:button.attributes-copy-button
|
||||
{:on-click #(wapi/write-to-clipboard (str/trim text))}
|
||||
i/copy]]
|
||||
|
||||
(when (or (:fill-color style) (:fill-color-gradient style))
|
||||
[:& color-row {:format @color-format
|
||||
:on-change-format #(reset! color-format %)
|
||||
:color (shape->color style)
|
||||
:on-copy (copy-cb style
|
||||
[:fill-color :fill-color-gradient]
|
||||
:to-prop to-prop
|
||||
:format (format-style color))}])
|
||||
|
||||
(when (:font-id style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-family")]
|
||||
[:div.attributes-value (-> style :font-id fonts/get-font-data :name)]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-family :format identity)} i/copy]])
|
||||
|
||||
(when (:font-style style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-style")]
|
||||
[:div.attributes-value (str (:font-style style))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-style :format identity)} i/copy]])
|
||||
|
||||
(when (:font-size style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-size")]
|
||||
[:div.attributes-value (str (:font-size style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :font-size :format #(str % "px"))} i/copy]])
|
||||
|
||||
(when (:line-height style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.line-height")]
|
||||
[:div.attributes-value (str (:line-height style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :line-height :format #(str % "px"))} i/copy]])
|
||||
|
||||
(when (:letter-spacing style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.letter-spacing")]
|
||||
[:div.attributes-value (str (:letter-spacing style)) "px"]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :letter-spacing :format #(str % "px"))} i/copy]])
|
||||
|
||||
(when (:text-decoration style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-decoration")]
|
||||
[:div.attributes-value (->> style :text-decoration (str "handoff.attributes.typography.text-decoration.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :text-decoration :format name)} i/copy]])
|
||||
|
||||
(when (:text-transform style)
|
||||
[:div.attributes-unit-row
|
||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-transform")]
|
||||
[:div.attributes-value (->> style :text-transform (str "handoff.attributes.typography.text-transform.") (t locale))]
|
||||
[:button.attributes-copy-button {:on-click (copy-cb style :text-transform :format name)} i/copy]])]))
|
||||
|
||||
|
||||
(mf/defc text-block [{:keys [shape locale]}]
|
||||
(let [font (ut/search-text-attrs (:content shape)
|
||||
(keys ut/default-text-attrs))
|
||||
|
||||
style-text-blocks (->> (keys ut/default-text-attrs)
|
||||
(ut/parse-style-text-blocks (:content shape))
|
||||
(remove (fn [[style text]] (str/empty? (str/trim text))))
|
||||
(mapv (fn [[style text]] (vector (merge ut/default-text-attrs style) text))))
|
||||
|
||||
font (merge ut/default-text-attrs font)]
|
||||
(for [[idx [full-style text]] (map-indexed vector style-text-blocks)]
|
||||
(let [previus-style (first (nth style-text-blocks (dec idx) nil))
|
||||
style (d/remove-equal-values full-style previus-style)
|
||||
|
||||
;; If the color is set we need to add opacity otherwise the display will not work
|
||||
style (cond-> style
|
||||
(:fill-color style)
|
||||
(assoc :fill-opacity (:fill-opacity full-style)))]
|
||||
[:& typography-block {:shape shape
|
||||
:locale locale
|
||||
:full-style full-style
|
||||
:style style
|
||||
:text text}]))))
|
||||
|
||||
(mf/defc text-panel [{:keys [shapes locale]}]
|
||||
(let [shapes (->> shapes (filter has-text?))]
|
||||
(when (seq shapes)
|
||||
[:div.attributes-block
|
||||
[:div.attributes-block-title
|
||||
[:div.attributes-block-title-text (t locale "handoff.attributes.typography")]]
|
||||
|
||||
(for [shape shapes]
|
||||
[:& text-block {:shape shape
|
||||
:locale locale}])])))
|
||||
|
|
@ -7,7 +7,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.layers-sidebar
|
||||
(ns app.main.ui.viewer.handoff.left-sidebar
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[okulary.core :as l]
|
||||
|
@ -45,29 +45,22 @@
|
|||
toggle-collapse
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(if (and expanded? (kbd/shift? event))
|
||||
(st/emit! (dv/collapse-all))
|
||||
(st/emit! (dv/toggle-collapse id))))
|
||||
(st/emit! (dv/toggle-collapse id)))
|
||||
|
||||
select-shape
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(let [id (:id item)]
|
||||
(st/emit! (dv/select-shape id))
|
||||
#_(cond
|
||||
(or (:blocked item)
|
||||
(:hidden item))
|
||||
nil
|
||||
(cond
|
||||
(.-ctrlKey event)
|
||||
(st/emit! (dv/toggle-selection id))
|
||||
|
||||
(.-shiftKey event)
|
||||
(st/emit! (dv/select-shape id true))
|
||||
(st/emit! (dv/shift-select-to id))
|
||||
|
||||
(> (count selected) 1)
|
||||
(st/emit! (dv/deselect-all)
|
||||
(dv/select-shape id))
|
||||
:else
|
||||
(st/emit! (dv/deselect-all)
|
||||
(dv/select-shape id)))))
|
||||
(st/emit! (dv/select-shape id)))
|
||||
))
|
||||
]
|
||||
|
||||
(mf/use-effect
|
||||
|
@ -105,7 +98,7 @@
|
|||
:objects objects
|
||||
:key (:id item)}]))])]))
|
||||
|
||||
(mf/defc layers-sidebar [{:keys [frame]}]
|
||||
(mf/defc left-sidebar [{:keys [frame]}]
|
||||
(let [page (mf/deref page-ref)
|
||||
selected (mf/deref selected-shapes)
|
||||
objects (:objects page)]
|
|
@ -44,10 +44,17 @@
|
|||
(st/emit! (dv/hover-shape id hover?)))))
|
||||
|
||||
(defn select-shape [{:keys [type id]}]
|
||||
#(when-not (#{:group :frame} type)
|
||||
(dom/prevent-default %)
|
||||
(dom/stop-propagation %)
|
||||
(st/emit! (dv/select-shape id))))
|
||||
(fn [event]
|
||||
(when-not (#{:group :frame} type)
|
||||
(do
|
||||
(dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(cond
|
||||
(.-shiftKey event)
|
||||
(st/emit! (dv/toggle-selection id))
|
||||
|
||||
:else
|
||||
(st/emit! (dv/select-shape id)))))))
|
||||
|
||||
(defn shape-wrapper-factory
|
||||
[component]
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns app.main.ui.viewer.handoff.attributes-sidebar
|
||||
(ns app.main.ui.viewer.handoff.right-sidebar
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[okulary.core :as l]
|
||||
|
@ -15,7 +15,7 @@
|
|||
[app.main.store :as st]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||
[app.main.ui.viewer.handoff.attrib-panel :refer [attrib-panel]]
|
||||
[app.main.ui.viewer.handoff.attributes :refer [attributes]]
|
||||
[app.main.ui.workspace.sidebar.layers :refer [element-icon]]))
|
||||
|
||||
(defn make-selected-shapes-iref
|
||||
|
@ -28,14 +28,12 @@
|
|||
(mapv resolve-shape selected)))]
|
||||
#(l/derived selected->shapes st/state)))
|
||||
|
||||
(mf/defc info-panel [{:keys [frame shapes]}]
|
||||
(if (> (count shapes) 1)
|
||||
;; TODO:Multiple selection
|
||||
nil
|
||||
;; Single shape
|
||||
(when-let [shape (first shapes)]
|
||||
(let [options
|
||||
(case (:type shape)
|
||||
(mf/defc attributes-panel [{:keys [frame shapes]}]
|
||||
(let [type (if (= (count shapes) 1)
|
||||
(-> shapes first :type)
|
||||
:multiple)]
|
||||
(let [options (case type
|
||||
:multiple [:fill :stroke :image :text :shadow :blur]
|
||||
:frame [:layout :fill]
|
||||
:group [:layout]
|
||||
:rect [:layout :fill :stroke :shadow :blur]
|
||||
|
@ -43,15 +41,15 @@
|
|||
:path [:layout :fill :stroke :shadow :blur]
|
||||
:curve [:layout :fill :stroke :shadow :blur]
|
||||
:image [:image :layout :shadow :blur]
|
||||
:text [:layout :typography :shadow :blur])]
|
||||
[:& attrib-panel {:frame frame
|
||||
:shape shape
|
||||
:options options}]))))
|
||||
:text [:layout :text :shadow :blur])]
|
||||
[:& attributes {:frame frame
|
||||
:shapes shapes
|
||||
:options options}])))
|
||||
|
||||
(mf/defc code-panel []
|
||||
[:div.element-options])
|
||||
|
||||
(mf/defc attributes-sidebar [{:keys [frame]}]
|
||||
(mf/defc right-sidebar [{:keys [frame]}]
|
||||
(let [locale (mf/deref i18n/locale)
|
||||
section (mf/use-state :info #_:code)
|
||||
selected-ref (mf/use-memo (make-selected-shapes-iref))
|
||||
|
@ -74,7 +72,7 @@
|
|||
[:& tab-container {:on-change-tab #(reset! section %)
|
||||
:selected @section}
|
||||
[:& tab-element {:id :info :title (t locale "handoff.tabs.info")}
|
||||
[:& info-panel {:frame frame
|
||||
[:& attributes-panel {:frame frame
|
||||
:shapes shapes}]]
|
||||
|
||||
[:& tab-element {:id :code :title (t locale "handoff.tabs.code")}
|
Loading…
Add table
Reference in a new issue