0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-26 14:41:36 -05:00

Refactor shapes attributes handling.

This commit is contained in:
Andrey Antukh 2017-01-05 23:21:05 +01:00
parent b3d995de53
commit 28d18a07a0
No known key found for this signature in database
GPG key ID: 4DFEBCB8316A8B95
14 changed files with 440 additions and 543 deletions

View file

@ -90,18 +90,6 @@
(rx/map (fn [{:keys [x y] :as pt}] (rx/map (fn [{:keys [x y] :as pt}]
(apply-temporal-displacement id (gpt/subtract pt point1))))))))) (apply-temporal-displacement id (gpt/subtract pt point1)))))))))
(defn update-line-attrs
[sid {:keys [x1 y1 x2 y2] :as opts}]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(let [shape (get-in state [:shapes sid])
props (select-keys opts [:x1 :y1 :x2 :y2])
props' (select-keys shape [:x1 :y1 :x2 :y2])]
(update-in state [:shapes sid] geom/setup
(merge props' props))))))
(defn update-rotation (defn update-rotation
[sid rotation] [sid rotation]
{:pre [(number? rotation) {:pre [(number? rotation)
@ -236,57 +224,68 @@
(update [_ state] (update [_ state]
(assoc-in state [:shapes sid :content] content)))) (assoc-in state [:shapes sid :content] content))))
(defn update-fill-attrs ;; --- Update Shape Attrs
[sid {:keys [color opacity] :as opts}]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes sid]
merge
(when color {:fill color})
(when opacity {:fill-opacity opacity})))))
(defn update-font-attrs (declare UpdateAttrs)
[sid {:keys [family style weight size align
letter-spacing line-height] :as opts}]
(reify
udp/IPageUpdate
ptk/UpdateEvent
(update [_ state]
(update-in state [:shapes sid :font]
merge
(when line-height {:line-height line-height})
(when letter-spacing {:letter-spacing letter-spacing})
(when align {:align align})
(when family {:family family})
(when style {:style style})
(when weight {:weight weight})
(when size {:size size})))))
(defn update-stroke-attrs (deftype UpdateAttrs [id attrs]
[sid {:keys [color opacity type width] :as opts}] ptk/WatchEvent
(reify (watch [_ state stream]
udp/IPageUpdate (println "update-attrs" id attrs)
ptk/UpdateEvent (let [{:keys [type] :as shape} (get-in state [:shapes id])]
(update [_ state] (if (= type :group)
(update-in state [:shapes sid] (rx/from-coll (map #(UpdateAttrs. % attrs) (:items shape)))
merge (rx/of #(update-in % [:shapes id] merge attrs))))))
(when type {:stroke-type type})
(when width {:stroke-width width})
(when color {:stroke color})
(when opacity {:stroke-opacity opacity})))))
(defn update-radius-attrs (s/def ::fill-color us/color?)
[sid {:keys [rx ry] :as opts}] (s/def ::fill-opacity number?)
(reify (s/def ::line-height number?)
udp/IPageUpdate (s/def ::letter-spacing number?)
ptk/UpdateEvent (s/def ::text-align #{"left" "right" "center" "justify"})
(update [_ state] (s/def ::font-family string?)
(update-in state [:shapes sid] (s/def ::font-style string?)
merge (s/def ::font-weight string?)
(when rx {:rx rx}) (s/def ::font-size number?)
(when ry {:ry ry}))))) (s/def ::stroke-style #{:none :solid :dotted :dashed :mixed})
(s/def ::stroke-width number?)
(s/def ::stroke-color us/color?)
(s/def ::stroke-opacity number?)
(s/def ::rx number?)
(s/def ::ry number?)
(s/def ::proportion number?)
(s/def ::proportion-lock boolean?)
(s/def ::collapsed boolean?)
(s/def ::hidden boolean?)
(s/def ::blocked boolean?)
(s/def ::locked boolean?)
(s/def ::shape-attrs
(s/keys :opt-un [::fill-color
::fill-opacity
::line-height
::letter-spacing
::text-align
::font-family
::font-style
::font-weight
::font-size
::stroke-style
::stroke-width
::stroke-color
::stroke-opacity
::rx ::ry
::proportion-lock
::proportion
::collapsed
::hidden
::blocked
::locked]))
(defn update-attrs
[id attrs]
{:pre [(uuid? id) (us/valid? ::shape-attrs attrs)]}
(let [atts (us/extract attrs ::shape-attrs)]
(UpdateAttrs. id attrs)))
;; --- Shape Proportions ;; --- Shape Proportions
@ -629,27 +628,16 @@
(rx/from-coll (rx/from-coll
(into [(deselect-all)] (map #(delete-shape %) selected))))))) (into [(deselect-all)] (map #(delete-shape %) selected)))))))
(defn update-selected-shapes-fill (deftype UpdateSelectedShapesAttrs [attrs]
"Update the fill related attributed on ptk/WatchEvent
selected shapes." (watch [_ state stream]
[opts] (let [xf (map #(update-attrs % attrs))]
(reify (rx/from-coll (sequence xf (get-in state [:workspace :selected]))))))
ptk/WatchEvent
(watch [_ state stream]
(->> (get-in state [:workspace :selected])
(map #(update-fill-attrs % opts))
(rx/from-coll)))))
(defn update-selected-shapes-stroke (defn update-selected-shapes-attrs
"Update the fill related attributed on [attrs]
selected shapes." {:pre [(us/valid? ::shape-attrs attrs)]}
[opts] (UpdateSelectedShapesAttrs. attrs))
(reify
ptk/WatchEvent
(watch [_ state s]
(rx/from-coll
(->> (get-in state [:workspace :selected])
(map #(update-stroke-attrs % opts)))))))
;; --- Move Selected Layer ;; --- Move Selected Layer

View file

@ -2,47 +2,55 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2016-2017 Andrey Antukh <niwi@niwi.nz>
(ns uxbox.main.ui.shapes.attrs) (ns uxbox.main.ui.shapes.attrs)
(def ^:private +style-attrs+ (def shape-style-attrs
#{:fill :fill-opacity :opacity #{:fill-color
:stroke :stroke-opacity :stroke-width :fill-opacity
:stroke-type :rx :ry}) :stroke-color
:stroke-opacity
:stroke-width
:stroke-style
:rx
:ry})
(defn- transform-stroke-type (def shape-default-attrs
{:stroke-color "#000000"
:stroke-opacity 1
:fill-color "#000000"
:fill-opacity 1})
(defn- stroke-type->dasharray
[style]
(case style
:mixed "5,5,1,5"
:dotted "5,5"
:dashed "10,10"))
(defn- rename-attr
[[key value :as pair]]
(case key
:stroke-color [:stroke value]
:fill-color [:fill value]
pair))
(defn- rename-attrs
[attrs] [attrs]
(if-let [type (:stroke-type attrs)] (into {} (map rename-attr) attrs))
(let [value (case type
:mixed "5,5,1,5"
:dotted "5,5"
:dashed "10,10"
nil)]
(if value
(-> attrs
(assoc! :stroke-dasharray value)
(dissoc! :stroke-type))
(dissoc! attrs :stroke-type)))
attrs))
(defn- transform-stroke-attrs (defn- transform-stroke-attrs
[attrs] [{:keys [stroke-style] :or {stroke-style :none} :as attrs}]
(if (= (:stroke-type attrs :none) :none) (if (= stroke-style :none)
(dissoc! attrs :stroke-type :stroke-width :stroke-opacity :stroke) (dissoc attrs :stroke-style :stroke-width :stroke-opacity :stroke-color)
(transform-stroke-type attrs))) (-> (merge shape-default-attrs attrs)
(assoc :stroke-dasharray (stroke-type->dasharray stroke-style))
(dissoc :stroke-style))))
(defn- extract-style-attrs (defn- extract-style-attrs
"Extract predefinet attrs from shapes." "Extract predefinet attrs from shapes."
[shape] [shape]
(let [attrs (select-keys shape +style-attrs+)] (-> (select-keys shape shape-style-attrs)
(-> (transient attrs) (transform-stroke-attrs)
(transform-stroke-attrs) (rename-attrs)))
(persistent!))))
(defn- make-debug-attrs
[shape]
(let [attrs (select-keys shape [:rotation :width :height :x :y])
xf (map (fn [[x v]]
[(keyword (str "data-" (name x))) v]))]
(into {} xf attrs)))

View file

@ -69,9 +69,7 @@
(let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix)) (let [xfmt (cond-> (or tmp-resize-xform (gmt/matrix))
tmp-displacement (gmt/translate tmp-displacement)) tmp-displacement (gmt/translate tmp-displacement))
props {:id (str id) :transform (str xfmt)} attrs {:id (str id) :transform (str xfmt)}]
attrs (merge props (attrs/extract-style-attrs shape))]
[:g attrs [:g attrs
(for [item (reverse items) (for [item (reverse items)
:let [key (str item)]] :let [key (str item)]]

View file

@ -31,6 +31,7 @@
;; --- Rect Shape ;; --- Rect Shape
(defn- rotate (defn- rotate
;; TODO: revisit, i'm not sure if this function is duplicated.
[mt {:keys [x1 y1 x2 y2 width height rotation] :as shape}] [mt {:keys [x1 y1 x2 y2 width height rotation] :as shape}]
(let [x-center (+ x1 (/ width 2)) (let [x-center (+ x1 (/ width 2))
y-center (+ y1 (/ height 2)) y-center (+ y1 (/ height 2))

View file

@ -35,6 +35,7 @@
;; --- Text Component ;; --- Text Component
(declare text-shape) (declare text-shape)
(declare text-shape-wrapper)
(declare text-shape-edit) (declare text-shape-edit)
(mx/defcs text-component (mx/defcs text-component
@ -55,7 +56,7 @@
:on-mouse-down on-mouse-down} :on-mouse-down on-mouse-down}
(if edition? (if edition?
(text-shape-edit shape) (text-shape-edit shape)
(text-shape shape))]))) (text-shape-wrapper shape))])))
;; --- Text Styles Helpers ;; --- Text Styles Helpers
@ -66,28 +67,36 @@
:stroke-opacity "0.4"}}) :stroke-opacity "0.4"}})
(defn- make-style (defn- make-style
[{:keys [font fill opacity] [{:keys [fill-color
:or {fill "#000000" opacity 1}}] fill-opacity
(let [{:keys [family weight style size align font-family
line-height letter-spacing] font-weight
:or {family "sourcesanspro" font-style
weight "normal" font-size
style "normal" text-align
line-height 1.4 line-height
letter-spacing 1 letter-spacing]
align "left" :or {fill-color "#000000"
size 16}} font fill-opacity 1
color (-> fill font-family "sourcesanspro"
(color/hex->rgba opacity) font-weight "normal"
font-style "normal"
fobt-size 16
line-height 1.4
letter-spacing 1
text-align "left"}
:as shape}]
(let [color (-> fill-color
(color/hex->rgba fill-opacity)
(color/rgb->str))] (color/rgb->str))]
(merge (merge
{:fontSize (str size "px") {:fontSize (str font-size "px")
:color color :color fill-color
:whiteSpace "pre" :whiteSpace "pre-wrap"
:textAlign align :textAlign text-align
:fontFamily family :fontFamily font-family
:fontWeight weight :fontWeight font-weight
:fontStyle style} :fontStyle font-style}
(when line-height {:lineHeight line-height}) (when line-height {:lineHeight line-height})
(when letter-spacing {:letterSpacing letter-spacing})))) (when letter-spacing {:letterSpacing letter-spacing}))))
@ -105,11 +114,9 @@
{:did-mount text-shape-edit-did-mount {:did-mount text-shape-edit-did-mount
:mixins [mx/static]} :mixins [mx/static]}
[{:keys [id x1 y1 content] :as shape}] [{:keys [id x1 y1 content] :as shape}]
(let [size (geom/size shape) (let [{:keys [width height]} (geom/size shape)
style (make-style shape) style (make-style shape)
;; rfm (geom/transformation-matrix shape) props {:x x1 :y y1 :width width :height height}]
props {:x x1 :y y1} ;; :transform (str rfm)}
props (merge props size)]
(letfn [#_(on-blur [ev] (letfn [#_(on-blur [ev]
(rlocks/release! :ui/text-edit) (rlocks/release! :ui/text-edit)
(on-done)) (on-done))
@ -117,15 +124,15 @@
(let [content (dom/event->inner-text ev)] (let [content (dom/event->inner-text ev)]
(st/emit! (uds/update-text id {:content content}))))] (st/emit! (uds/update-text id {:content content}))))]
[:g [:g
[:rect (merge props +select-rect-attrs+)] #_[:rect (merge props +select-rect-attrs+)]
[:foreignObject props [:foreignObject props
[:p {:ref "container" [:div {:style style}
;; :on-blur on-blur [:p {:ref "container"
:on-input on-input ;; :on-blur on-blur
:contentEditable true :on-input on-input
:style style}]]]))) :contentEditable true}]]]])))
;; --- Text Shape ;; --- Text Shape Wrapper
;; NOTE: this is a hack for the browser rendering. ;; NOTE: this is a hack for the browser rendering.
;; ;;
@ -135,30 +142,46 @@
;; completelly invisible. The complete dom rerender fixes that ;; completelly invisible. The complete dom rerender fixes that
;; problem. ;; problem.
(defn- text-shape-did-update (defn text-shape-wrapper-did-mount
[own] [own]
(let [pref (mx/ref-node own "fo") (let [[shape] (:rum/args own)
html (.-innerHTML pref)] dom (mx/ref-node own "fobject")
(set! (.-innerHTML pref) html) html (mx/render-static-html (text-shape shape))]
(set! (.-innerHTML dom) html))
own)
(defn text-shape-wrapper-did-remount
[old own]
(let [[old-shape] (:rum/args old)
[shape] (:rum/args own)]
(when (not= shape old-shape)
(let [dom (mx/ref-node own "fobject")
html (mx/render-static-html (text-shape shape))]
(set! (.-innerHTML dom) html)))
own)) own))
(mx/defc text-shape (mx/defc text-shape-wrapper
{:mixins [mx/static] {:mixins [mx/static]
:did-update text-shape-did-update} :did-mount text-shape-wrapper-did-mount
[{:keys [tmp-resize-xform] :as shape}] :did-remount text-shape-wrapper-did-remount}
(let [shape (cond-> (geom/size shape) [{:keys [id tmp-resize-xform tmp-displacement] :as shape}]
tmp-resize-xform (geom/transform shape tmp-resize-xform)) (let [xfmt (cond-> (gmt/matrix)
tmp-displacement (gmt/translate tmp-displacement)
tmp-resize-xform (gmt/multiply tmp-resize-xform))
{:keys [id x1 y1 content {:keys [x1 y1 width height] :as shape} (-> (geom/transform shape xfmt)
width height (geom/size))]
tmp-displacement]} (geom/size shape) [:foreignObject {:x x1
:y y1
:id (str id)
:ref "fobject"
:width width
:height height}]))
xfmt (cond-> (gmt/matrix) ;; --- Text Shape
tmp-displacement (gmt/translate tmp-displacement))
style (make-style shape) (mx/defc text-shape
props {:x x1 :y y1 :id (str id) :ref "fo" [{:keys [content] :as shape}]
:width width :height height (let [style (make-style shape)]
:transform (str xfmt)}] [:div {:style style}
[:foreignObject props [:p content]]))
[:p {:ref "p" :style style} content]]))

View file

@ -33,10 +33,10 @@
{:mixins [mx/static]} {:mixins [mx/static]}
[color] [color]
(letfn [(select-color [event] (letfn [(select-color [event]
(dom/prevent-default event) (let [attrs (if (kbd/shift? event)
(if (kbd/shift? event) {:stroke-color color}
(st/emit! (uds/update-selected-shapes-stroke {:color color})) {:fill-color color})]
(st/emit! (uds/update-selected-shapes-fill {:color color}))))] (st/emit! (uds/update-selected-shapes-attrs attrs))))]
(let [rgb-vec (hex->rgb color) (let [rgb-vec (hex->rgb color)
rgb-color (apply str "" (interpose ", " rgb-vec))] rgb-color (apply str "" (interpose ", " rgb-vec))]
[:div.color-cell {:key (str color) [:div.color-cell {:key (str color)

View file

@ -32,15 +32,11 @@
(mx/defcs shape-colorpicker (mx/defcs shape-colorpicker
{:mixins [mx/reactive mx/static]} {:mixins [mx/reactive mx/static]}
[own {:keys [x y shape attr] :as opts}] [own {:keys [x y shape attr] :as opts}]
(let [shape (mx/react (focus-shape shape)) (let [{:keys [id] :as shape} (mx/react (focus-shape shape))
left (- x 260) left (- x 260)
top (- y 50)] top (- y 50)]
(letfn [(change-color [color] (letfn [(change-color [color]
(let [attrs {:color color}] (st/emit! (uds/update-attrs id {attr color})))]
(st/emit!
(case attr
:stroke (uds/update-stroke-attrs (:id shape) attrs)
:fill (uds/update-fill-attrs (:id shape) attrs)))))]
[:div.colorpicker-tooltip [:div.colorpicker-tooltip
{:style {:left (str left "px") {:style {:left (str left "px")
:top (str top "px")}} :top (str top "px")}}

View file

@ -16,11 +16,10 @@
[uxbox.main.data.shapes :as uds] [uxbox.main.data.shapes :as uds]
[uxbox.main.ui.workspace.base :as wb] [uxbox.main.ui.workspace.base :as wb]
[uxbox.main.ui.icons :as i] [uxbox.main.ui.icons :as i]
[uxbox.util.mixins :as mx :include-macros true] [uxbox.main.ui.shapes.attrs :refer [shape-default-attrs]]
[uxbox.main.ui.workspace.sidebar.options.icon-measures :as options-iconm] [uxbox.main.ui.workspace.sidebar.options.icon-measures :as options-iconm]
[uxbox.main.ui.workspace.sidebar.options.circle-measures :as options-circlem] [uxbox.main.ui.workspace.sidebar.options.circle-measures :as options-circlem]
[uxbox.main.ui.workspace.sidebar.options.rect-measures :as options-rectm] [uxbox.main.ui.workspace.sidebar.options.rect-measures :as options-rectm]
[uxbox.main.ui.workspace.sidebar.options.line-measures :as options-linem]
[uxbox.main.ui.workspace.sidebar.options.fill :as options-fill] [uxbox.main.ui.workspace.sidebar.options.fill :as options-fill]
[uxbox.main.ui.workspace.sidebar.options.text :as options-text] [uxbox.main.ui.workspace.sidebar.options.text :as options-text]
[uxbox.main.ui.workspace.sidebar.options.stroke :as options-stroke] [uxbox.main.ui.workspace.sidebar.options.stroke :as options-stroke]
@ -28,19 +27,19 @@
[uxbox.main.ui.workspace.sidebar.options.interactions :as options-interactions] [uxbox.main.ui.workspace.sidebar.options.interactions :as options-interactions]
[uxbox.main.geom :as geom] [uxbox.main.geom :as geom]
[uxbox.util.dom :as dom] [uxbox.util.dom :as dom]
[uxbox.util.data :as data])) [uxbox.util.data :as data]
[uxbox.util.mixins :as mx :include-macros true]))
;; --- Constants ;; --- Constants
(def ^:private +menus-map+ (def ^:private +menus-map+
{:icon [::icon-measures ::fill ::stroke ::interactions] {:icon [::icon-measures ::fill ::stroke ::interactions]
:rect [::rect-measures ::fill ::stroke ::interactions] :rect [::rect-measures ::fill ::stroke ::interactions]
:line [::line-measures ::stroke ::interactions]
:path [::fill ::stroke ::interactions] :path [::fill ::stroke ::interactions]
:circle [::circle-measures ::fill ::stroke ::interactions] :circle [::circle-measures ::fill ::stroke ::interactions]
:text [::fill ::text ::interactions] :text [::fill ::text ::interactions]
:image [::interactions] :image [::interactions]
:group [::interactions] :group [::fill ::stroke ::interactions]
::page [::page-measures ::page-grid-options]}) ::page [::page-measures ::page-grid-options]})
(def ^:private +menus+ (def ^:private +menus+
@ -52,10 +51,6 @@
:id ::rect-measures :id ::rect-measures
:icon i/infocard :icon i/infocard
:comp options-rectm/rect-measures-menu} :comp options-rectm/rect-measures-menu}
{:name "Size, position & rotation"
:id ::line-measures
:icon i/infocard
:comp options-linem/line-measures-menu}
{:name "Size, position & rotation" {:name "Size, position & rotation"
:id ::circle-measures :id ::circle-measures
:icon i/infocard :icon i/infocard
@ -128,7 +123,8 @@
(mx/defc options-toolbox (mx/defc options-toolbox
{:mixins [mx/static mx/reactive]} {:mixins [mx/static mx/reactive]}
[] []
(let [shape (mx/react selected-shape-ref) (let [shape (->> (mx/react selected-shape-ref)
(merge shape-default-attrs))
close #(st/emit! (udw/toggle-flag :element-options))] close #(st/emit! (udw/toggle-flag :element-options))]
[:div.elementa-options.tool-window [:div.elementa-options.tool-window
[:div.tool-window-bar [:div.tool-window-bar

View file

@ -2,13 +2,11 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.options.fill (ns uxbox.main.ui.workspace.sidebar.options.fill
(:require [sablono.core :as html :refer-macros [html]] (:require [lentes.core :as l]
[rum.core :as rum]
[lentes.core :as l]
[uxbox.util.i18n :refer (tr)] [uxbox.util.i18n :refer (tr)]
[uxbox.util.router :as r] [uxbox.util.router :as r]
[potok.core :as ptk] [potok.core :as ptk]
@ -21,59 +19,51 @@
[uxbox.util.data :refer (parse-int parse-float read-string)] [uxbox.util.data :refer (parse-int parse-float read-string)]
[uxbox.util.spec :refer (color?)])) [uxbox.util.spec :refer (color?)]))
(defn fill-menu-render (mx/defc fill-menu
[own menu shape] {:mixins [mx/static]}
(letfn [(change-fill [value] [menu {:keys [id] :as shape}]
(let [sid (:id shape)] (letfn [(change-attrs [attrs]
(st/emit! (uds/update-fill-attrs sid value)))) (st/emit! (uds/update-attrs id attrs)))
(on-color-change [event] (on-color-change [event]
(let [value (dom/event->value event)] (let [value (dom/event->value event)]
(when (color? value) (when (color? value)
(change-fill {:color value})))) (change-attrs {:fill-color value}))))
(on-opacity-change [event] (on-opacity-change [event]
(let [value (dom/event->value event) (let [value (dom/event->value event)
value (parse-float value 1) value (parse-float value 1)
value (/ value 10000)] value (/ value 10000)]
(change-fill {:opacity value}))) (change-attrs {:fill-opacity value})))
(on-color-picker-event [color] (on-color-picker-event [color]
(change-fill {:color color})) (change-attrs {:fill-color color}))
(show-color-picker [event] (show-color-picker [event]
(let [x (.-clientX event) (let [x (.-clientX event)
y (.-clientY event) y (.-clientY event)
opts {:x x :y y opts {:x x :y y
:shape (:id shape) :shape (:id shape)
:attr :fill :attr :fill-color
:transparent? true}] :transparent? true}]
(udl/open! :workspace/shape-colorpicker opts)))] (udl/open! :workspace/shape-colorpicker opts)))]
[:div.element-set {:key (str (:id menu))}
[:div.element-set-title (:name menu)]
[:div.element-set-content
(html [:span "Color"]
[:div.element-set {:key (str (:id menu))} [:div.row-flex.color-data
[:div.element-set-title (:name menu)] [:span.color-th
[:div.element-set-content {:style {:background-color (:fill-color shape)}
:on-click show-color-picker}]
[:div.color-info
[:input
{:on-change on-color-change
:value (:fill-color shape)}]]]
[:span "Color"] ;; SLIDEBAR FOR ROTATION AND OPACITY
[:div.row-flex.color-data [:span "Opacity"]
[:span.color-th [:div.row-flex
{:style {:background-color (:fill shape "#000000")} [:input.slidebar
:on-click show-color-picker}] {:type "range"
[:div.color-info :min "0"
[:input :max "10000"
{:on-change on-color-change :value (* 10000 (:fill-opacity shape))
:value (:fill shape "#000000")}]]] :step "1"
:on-change on-opacity-change}]]]]))
;; SLIDEBAR FOR ROTATION AND OPACITY
[:span "Opacity"]
[:div.row-flex
[:input.slidebar
{:type "range"
:min "0"
:max "10000"
:value (* 10000 (:fill-opacity shape 1))
:step "1"
:on-change on-opacity-change}]]]])))
(def fill-menu
(mx/component
{:render fill-menu-render
:name "fill-menu"
:mixins [mx/static]}))

View file

@ -1,94 +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/.
;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.options.line-measures
(:require [sablono.core :as html :refer-macros [html]]
[rum.core :as rum]
[lentes.core :as l]
[uxbox.util.i18n :refer (tr)]
[uxbox.util.router :as r]
[potok.core :as ptk]
[uxbox.store :as st]
[uxbox.main.data.workspace :as udw]
[uxbox.main.data.shapes :as uds]
[uxbox.main.ui.icons :as i]
[uxbox.util.mixins :as mx :include-macros true]
[uxbox.main.geom :as geom]
[uxbox.util.dom :as dom]
[uxbox.util.math :refer (precision-or-0)]
[uxbox.util.data :refer (parse-int parse-float read-string)]))
(defn- line-measures-menu-render
[own menu shape]
(letfn [(on-rotation-change [event]
(let [value (dom/event->value event)
value (parse-int value 0)
sid (:id shape)]
(st/emit! (uds/update-rotation sid value))))
(on-pos-change [attr event]
(let [value (dom/event->value event)
value (parse-int value nil)
sid (:id shape)
props {attr value}]
(st/emit! (uds/update-line-attrs sid props))))]
(html
[:div.element-set {:key (str (:id menu))}
[:div.element-set-title (:name menu)]
[:div.element-set-content
[:span "Position"]
[:div.row-flex
[:input.input-text
{:placeholder "x1"
:type "number"
:value (precision-or-0 (:x1 shape 0) 2)
:on-change (partial on-pos-change :x1)}]
[:input.input-text
{:placeholder "y1"
:type "number"
:value (precision-or-0 (:y1 shape 0) 2)
:on-change (partial on-pos-change :y1)}]]
[:div.row-flex
[:input.input-text
{:placeholder "x2"
:type "number"
:value (precision-or-0 (:x2 shape 0) 2)
:on-change (partial on-pos-change :x2)}]
[:input.input-text
{:placeholder "y2"
:type "number"
:value (precision-or-0 (:y2 shape 0) 2)
:on-change (partial on-pos-change :y2)}]]
[:span "Rotation"]
[:div.row-flex
[:input.slidebar
{:type "range"
:min 0
:max 360
:value (:rotation shape 0)
:on-change on-rotation-change}]]
[:div.row-flex
[:input.input-text
{:placeholder ""
:type "number"
:min 0
:max 360
:value (precision-or-0 (:rotation shape 0) 2)
:on-change on-rotation-change
}]
[:input.input-text
{:style {:visibility "hidden"}}]
]]]
)))
(def line-measures-menu
(mx/component
{:render line-measures-menu-render
:name "line-measures-menu"
:mixins [mx/static]}))

View file

@ -2,8 +2,8 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.options.rect-measures (ns uxbox.main.ui.workspace.sidebar.options.rect-measures
(:require [lentes.core :as l] (:require [lentes.core :as l]
@ -23,28 +23,22 @@
(mx/defc rect-measures-menu (mx/defc rect-measures-menu
{:mixins [mx/static]} {:mixins [mx/static]}
[menu {:keys [id] :as shape}] [menu {:keys [id] :as shape}]
(letfn [(on-size-change [attr event] (letfn [(on-size-change [event attr]
(let [value (-> (dom/event->value event) (parse-int 0)) (let [value (-> (dom/event->value event)
sid (:id shape) (parse-int 0))]
props {attr value}] (st/emit! (uds/update-size id {attr value}))))
(st/emit! (uds/update-size sid props))))
(on-rotation-change [event] (on-rotation-change [event]
(let [value (dom/event->value event) (let [value (-> (dom/event->value event)
value (parse-int value 0) (parse-int 0))]
sid (:id shape)] (st/emit! (uds/update-rotation id value))))
(st/emit! (uds/update-rotation sid value)))) (on-pos-change [event attr]
(on-pos-change [attr event] (let [value (-> (dom/event->value event)
(let [value (dom/event->value event) (parse-int nil))]
value (parse-int value nil) (st/emit! (uds/update-position id {attr value}))))
sid (:id shape) (on-border-change [event attr]
props {attr value}] (let [value (-> (dom/event->value event)
(st/emit! (uds/update-position sid props)))) (parse-int nil))]
(on-border-change [attr event] (st/emit! (uds/update-attrs id {attr value}))))
(let [value (dom/event->value event)
value (parse-int value nil)
sid (:id shape)
props {attr value}]
(st/emit! (uds/update-radius-attrs sid props))))
(on-proportion-lock-change [event] (on-proportion-lock-change [event]
(if (:proportion-lock shape) (if (:proportion-lock shape)
(st/emit! (uds/unlock-proportions id)) (st/emit! (uds/unlock-proportions id))
@ -62,7 +56,7 @@
:type "number" :type "number"
:min "0" :min "0"
:value (precision-or-0 (:width size) 2) :value (precision-or-0 (:width size) 2)
:on-change (partial on-size-change :width)}]] :on-change #(on-size-change % :width)}]]
[:div.lock-size [:div.lock-size
{:class (when (:proportion-lock shape) "selected") {:class (when (:proportion-lock shape) "selected")
:on-click on-proportion-lock-change} :on-click on-proportion-lock-change}
@ -73,7 +67,7 @@
:type "number" :type "number"
:min "0" :min "0"
:value (precision-or-0 (:height size) 2) :value (precision-or-0 (:height size) 2)
:on-change (partial on-size-change :height)}]]] :on-change #(on-size-change % :height)}]]]
[:span "Position"] [:span "Position"]
[:div.row-flex [:div.row-flex
@ -82,13 +76,13 @@
{:placeholder "x" {:placeholder "x"
:type "number" :type "number"
:value (precision-or-0 (:x1 shape 0) 2) :value (precision-or-0 (:x1 shape 0) 2)
:on-change (partial on-pos-change :x)}]] :on-change #(on-pos-change % :x)}]]
[:div.input-element.pixels [:div.input-element.pixels
[:input.input-text [:input.input-text
{:placeholder "y" {:placeholder "y"
:type "number" :type "number"
:value (precision-or-0 (:y1 shape 0) 2) :value (precision-or-0 (:y1 shape 0) 2)
:on-change (partial on-pos-change :y)}]]] :on-change #(on-pos-change % :y)}]]]
[:span "Border radius"] [:span "Border radius"]
[:div.row-flex [:div.row-flex
@ -96,13 +90,13 @@
{:placeholder "rx" {:placeholder "rx"
:type "number" :type "number"
:value (precision-or-0 (:rx shape 0) 2) :value (precision-or-0 (:rx shape 0) 2)
:on-change (partial on-border-change :rx)}] :on-change #(on-border-change % :rx)}]
[:div.lock-size i/lock] [:div.lock-size i/lock]
[:input.input-text [:input.input-text
{:placeholder "ry" {:placeholder "ry"
:type "number" :type "number"
:value (precision-or-0 (:ry shape 0) 2) :value (precision-or-0 (:ry shape 0) 2)
:on-change (partial on-border-change :ry)}]] :on-change #(on-border-change % :ry)}]]
[:span "Rotation"] [:span "Rotation"]
[:div.row-flex [:div.row-flex

View file

@ -2,13 +2,11 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.options.stroke (ns uxbox.main.ui.workspace.sidebar.options.stroke
(:require [sablono.core :as html :refer-macros [html]] (:require [lentes.core :as l]
[rum.core :as rum]
[lentes.core :as l]
[uxbox.util.i18n :refer (tr)] [uxbox.util.i18n :refer (tr)]
[uxbox.util.router :as r] [uxbox.util.router :as r]
[potok.core :as ptk] [potok.core :as ptk]
@ -22,81 +20,73 @@
[uxbox.util.math :refer (precision-or-0)] [uxbox.util.math :refer (precision-or-0)]
[uxbox.util.spec :refer (color?)])) [uxbox.util.spec :refer (color?)]))
(defn- stroke-menu-render (mx/defc stroke-menu
[own menu shape] {:mixed [mx/static]}
(letfn [(change-stroke [value] [menu {:keys [id] :as shape}]
(let [sid (:id shape)] (letfn [(update-attrs [attrs]
(st/emit! (uds/update-stroke-attrs sid value)))) (st/emit! (uds/update-attrs id attrs)))
(on-width-change [event] (on-width-change [event]
(let [value (dom/event->value event) (let [value (-> (dom/event->value event)
value (parse-float value 1)] (parse-float 1))]
(change-stroke {:width value}))) (update-attrs {:stroke-width value})))
(on-opacity-change [event] (on-opacity-change [event]
(let [value (dom/event->value event) (let [value (-> (dom/event->value event)
value (parse-float value 1) (parse-float 1)
value (/ value 10000)] (/ 10000))]
(change-stroke {:opacity value}))) (update-attrs {:stroke-opacity value})))
(on-stroke-style-change [event] (on-stroke-style-change [event]
(let [value (dom/event->value event) (let [value (-> (dom/event->value event)
value (read-string value)] (read-string))]
(change-stroke {:type value}))) (update-attrs {:stroke-style value})))
(on-stroke-color-change [event] (on-stroke-color-change [event]
(let [value (dom/event->value event)] (let [value (dom/event->value event)]
(when (color? value) (when (color? value)
(change-stroke {:color value})))) (update-attrs {:stroke-color value}))))
(show-color-picker [event] (show-color-picker [event]
(let [x (.-clientX event) (let [x (.-clientX event)
y (.-clientY event) y (.-clientY event)
opts {:x x :y y opts {:x x :y y
:shape (:id shape) :shape (:id shape)
:attr :stroke :attr :stroke-color
:transparent? true}] :transparent? true}]
(udl/open! :workspace/shape-colorpicker opts)))] (udl/open! :workspace/shape-colorpicker opts)))]
(let [local (:rum/local own)] [:div.element-set {:key (str (:id menu))}
(html [:div.element-set-title (:name menu)]
[:div.element-set {:key (str (:id menu))} [:div.element-set-content
[:div.element-set-title (:name menu)] [:span "Style"]
[:div.element-set-content [:div.row-flex
[:span "Style"] [:select#style.input-select {:placeholder "Style"
[:div.row-flex :value (pr-str (:stroke-style shape))
[:select#style.input-select {:placeholder "Style" :on-change on-stroke-style-change}
:value (:stroke-type shape) [:option {:value ":none"} "None"]
:on-change on-stroke-style-change} [:option {:value ":solid"} "Solid"]
[:option {:value ":none"} "None"] [:option {:value ":dotted"} "Dotted"]
[:option {:value ":solid"} "Solid"] [:option {:value ":dashed"} "Dashed"]
[:option {:value ":dotted"} "Dotted"] [:option {:value ":mixed"} "Mixed"]]
[:option {:value ":dashed"} "Dashed"] [:div.input-element.pixels
[:option {:value ":mixed"} "Mixed"]] [:input.input-text
[:div.input-element.pixels {:placeholder "Width"
[:input.input-text :type "number"
{:placeholder "Width" :min "0"
:type "number" :value (precision-or-0 (:stroke-width shape 1) 2)
:min "0" :on-change on-width-change}]]]
:value (precision-or-0 (:stroke-width shape 1) 2)
:on-change on-width-change}]]]
[:span "Color"] [:span "Color"]
[:div.row-flex.color-data [:div.row-flex.color-data
[:span.color-th [:span.color-th
{:style {:background-color (:stroke shape "#000000")} {:style {:background-color (:stroke-color shape)}
:on-click show-color-picker}] :on-click show-color-picker}]
[:div.color-info [:div.color-info
[:input [:input
{:on-change on-stroke-color-change {:on-change on-stroke-color-change
:value (:stroke shape "#000000")}]]] :value (:stroke-color shape)}]]]
[:span "Opacity"] [:span "Opacity"]
[:div.row-flex [:div.row-flex
[:input.slidebar [:input.slidebar
{:type "range" {:type "range"
:min "0" :min "0"
:max "10000" :max "10000"
:value (* 10000 (:stroke-opacity shape 1)) :value (* 10000 (:stroke-opacity shape))
:step "1" :step "1"
:on-change on-opacity-change}]]]])))) :on-change on-opacity-change}]]]]))
(def stroke-menu
(mx/component
{:render stroke-menu-render
:name "stroke-menu"
:mixed [mx/static]}))

View file

@ -2,13 +2,11 @@
;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; 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/. ;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;; ;;
;; Copyright (c) 2015-2016 Andrey Antukh <niwi@niwi.nz> ;; Copyright (c) 2015-2017 Andrey Antukh <niwi@niwi.nz>
;; Copyright (c) 2015-2016 Juan de la Cruz <delacruzgarciajuan@gmail.com> ;; Copyright (c) 2015-2017 Juan de la Cruz <delacruzgarciajuan@gmail.com>
(ns uxbox.main.ui.workspace.sidebar.options.text (ns uxbox.main.ui.workspace.sidebar.options.text
(:require [sablono.core :as html :refer-macros [html]] (:require [lentes.core :as l]
[rum.core :as rum]
[lentes.core :as l]
[uxbox.util.i18n :refer (tr)] [uxbox.util.i18n :refer (tr)]
[uxbox.util.router :as r] [uxbox.util.router :as r]
[potok.core :as ptk] [potok.core :as ptk]
@ -30,136 +28,128 @@
(declare +fonts+) (declare +fonts+)
(declare +fonts-by-id+) (declare +fonts-by-id+)
(defn- text-menu-render (mx/defc text-menu
[own menu {:keys [font] :as shape}] {:mixins [mx/static]}
(letfn [(on-font-family-change [event] [menu {:keys [id] :as shape}]
(letfn [(update-attrs [attrs]
(st/emit! (uds/update-attrs id attrs)))
(on-font-family-change [event]
(let [value (dom/event->value event) (let [value (dom/event->value event)
sid (:id shape) attrs {:font-family (read-string value)
params {:family (read-string value) :font-weight "normal"
:weight "normal" :font-style "normal"}]
:style "normal"}] (update-attrs attrs)))
(st/emit! (uds/update-font-attrs sid params))))
(on-font-size-change [event] (on-font-size-change [event]
(let [value (dom/event->value event) (let [value (-> (dom/event->value event)
params {:size (parse-int value)} (parse-int 0))]
sid (:id shape)] (update-attrs {:font-size value})))
(st/emit! (uds/update-font-attrs sid params))))
(on-font-letter-spacing-change [event] (on-font-letter-spacing-change [event]
(let [value (dom/event->value event) (let [value (-> (dom/event->value event)
params {:letter-spacing (parse-float value)} (parse-float))]
sid (:id shape)] (update-attrs {:letter-spacing value})))
(st/emit! (uds/update-font-attrs sid params))))
(on-font-line-height-change [event] (on-font-line-height-change [event]
(let [value (dom/event->value event) (let [value (-> (dom/event->value event)
params {:line-height (parse-float value)} (parse-float))]
sid (:id shape)] (update-attrs {:line-height value})))
(st/emit! (uds/update-font-attrs sid params))))
(on-font-align-change [event value] (on-font-align-change [event value]
(let [params {:align value} (update-attrs {:text-align value}))
sid (:id shape)]
(st/emit! (uds/update-font-attrs sid params))))
(on-font-style-change [event] (on-font-style-change [event]
(let [value (dom/event->value event) (let [[weight style] (-> (dom/event->value event)
[weight style] (read-string value) (read-string))]
sid (:id shape) (update-attrs {:font-style style
params {:style style :font-weight weight})))]
:weight weight}] (let [{:keys [font-family
(st/emit! (uds/update-font-attrs sid params))))] font-style
(let [{:keys [family style weight size align line-height letter-spacing] font-weight
:or {family "sourcesanspro" font-size
align "left" text-align
style "normal" line-height
weight "normal" letter-spacing]
:or {font-family "sourcesanspro"
font-style "normal"
font-weight "normal"
font-size 16
text-align "left"
letter-spacing 1 letter-spacing 1
line-height 1.4 line-height 1.4}} shape
size 16}} font styles (:styles (first (filter #(= (:id %) font-family) +fonts+)))]
styles (:styles (first (filter #(= (:id %) family) +fonts+)))] [:div.element-set {:key (str (:id menu))}
(html [:div.element-set-title (:name menu)]
[:div.element-set {:key (str (:id menu))} [:div.element-set-content
[:div.element-set-title (:name menu)]
[:div.element-set-content
[:span "Font family"] [:span "Font family"]
[:div.row-flex [:div.row-flex
[:select.input-select {:value (pr-str family) [:select.input-select {:value (pr-str font-family)
:on-change on-font-family-change} :on-change on-font-family-change}
(for [font +fonts+] (for [font +fonts+]
[:option {:value (pr-str (:id font)) [:option {:value (pr-str (:id font))
:key (:id font)} (:name font)])]] :key (:id font)} (:name font)])]]
[:span "Size and Weight"] [:span "Size and Weight"]
[:div.row-flex [:div.row-flex
[:div.editable-select [:div.editable-select
[:select.input-select [:select.input-select
{:id "common-font-sizes" {:id "common-font-sizes"
:on-change on-font-size-change} :value font-size
[:option {:value "8"} "8"] :on-change on-font-size-change}
[:option {:value "9"} "9"] [:option {:value "8"} "8"]
[:option {:value "10"} "10"] [:option {:value "9"} "9"]
[:option {:value "11"} "11"] [:option {:value "10"} "10"]
[:option {:value "12"} "12"] [:option {:value "11"} "11"]
[:option {:value "14"} "14"] [:option {:value "12"} "12"]
[:option {:value "18"} "18"] [:option {:value "14"} "14"]
[:option {:value "24"} "24"] [:option {:value "18"} "18"]
[:option {:value "36"} "36"] [:option {:value "24"} "24"]
[:option {:value "48"} "48"] [:option {:value "36"} "36"]
[:option {:value "72"} "72"]] [:option {:value "48"} "48"]
[:input.input-text [:option {:value "72"} "72"]]
{:placeholder "Font Size"
:type "number"
:min "0"
:max "200"
:value (precision-or-0 size 2)
:on-change on-font-size-change}]]
[:select.input-select {:value (pr-str [weight style])
:on-change on-font-style-change}
(for [style styles
:let [data (mapv #(get style %) [:weight :style])]]
[:option {:value (pr-str data)
:key (:name style)} (:name style)])]]
[:span "Line height and Letter spacing"]
[:div.row-flex
[:input.input-text [:input.input-text
{:placeholder "Line height" {:placeholder "Font Size"
:type "number" :type "number"
:step "0.1"
:min "0" :min "0"
:max "200" :max "200"
:value (precision-or-0 line-height 2) :value (precision-or-0 font-size 2)
:on-change on-font-line-height-change}] :on-change on-font-size-change}]]
[:input.input-text [:select.input-select {:value (pr-str [font-weight font-style])
{:placeholder "Letter spacing" :on-change on-font-style-change}
:type "number" (for [style styles
:step "0.1" :let [data (mapv #(get style %) [:weight :style])]]
:min "0" [:option {:value (pr-str data)
:max "200" :key (:name style)} (:name style)])]]
:value (precision-or-0 letter-spacing 2)
:on-change on-font-letter-spacing-change}]]
[:span "Line height and Letter spacing"]
[:div.row-flex
[:input.input-text
{:placeholder "Line height"
:type "number"
:step "0.1"
:min "0"
:max "200"
:value (precision-or-0 line-height 2)
:on-change on-font-line-height-change}]
[:input.input-text
{:placeholder "Letter spacing"
:type "number"
:step "0.1"
:min "0"
:max "200"
:value (precision-or-0 letter-spacing 2)
:on-change on-font-letter-spacing-change}]]
[:span "Text align"] [:span "Text align"]
[:div.row-flex.align-icons [:div.row-flex.align-icons
[:span {:class (when (= align "left") "current") [:span {:class (when (= text-align "left") "current")
:on-click #(on-font-align-change % "left")} :on-click #(on-font-align-change % "left")}
i/align-left] i/align-left]
[:span {:class (when (= align "right") "current") [:span {:class (when (= text-align "right") "current")
:on-click #(on-font-align-change % "right")} :on-click #(on-font-align-change % "right")}
i/align-right] i/align-right]
[:span {:class (when (= align "center") "current") [:span {:class (when (= text-align "center") "current")
:on-click #(on-font-align-change % "center")} :on-click #(on-font-align-change % "center")}
i/align-center] i/align-center]
[:span {:class (when (= align "justify") "current") [:span {:class (when (= text-align "justify") "current")
:on-click #(on-font-align-change % "justify")} :on-click #(on-font-align-change % "justify")}
i/align-justify]]]])))) i/align-justify]]]])))
(def text-menu
(mx/component
{:render text-menu-render
:name "text-menu"
:mixins [mx/static]}))
(def +fonts+ (def +fonts+
[{:id "sourcesanspro" [{:id "sourcesanspro"

View file

@ -45,3 +45,20 @@
(when-not valid (when-not valid
(js/console.error (str "Spec validation error:\n" (s/explain-str spec data)))) (js/console.error (str "Spec validation error:\n" (s/explain-str spec data))))
valid)) valid))
(defn extract
"Given a map spec, performs a `select-keys`
like exctraction from the object.
NOTE: this function does not executes
the conform or validation of the data,
is responsability of the user to do it."
[data spec]
(let [desc (s/describe spec)
{:keys [req-un opt-un]} (apply hash-map (rest desc))
keys (concat
(map (comp keyword name) req-un)
(map (comp keyword name) opt-un))]
(select-keys data keys)))