mirror of
https://github.com/penpot/penpot.git
synced 2025-05-17 09:16:10 +02:00
🎉 Add new text shape.
This commit is contained in:
parent
beed68166e
commit
aca1959dd7
11 changed files with 22141 additions and 468 deletions
218
frontend/src/uxbox/main/data/workspace/texts.cljs
Normal file
218
frontend/src/uxbox/main/data/workspace/texts.cljs
Normal file
|
@ -0,0 +1,218 @@
|
|||
;; 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 uxbox.main.data.workspace.texts
|
||||
(:require
|
||||
[beicon.core :as rx]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
[goog.events :as events]
|
||||
[goog.object :as gobj]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.main.fonts :as fonts]
|
||||
["slate" :as slate :refer [Editor Transforms Text]]))
|
||||
|
||||
(declare set-nodes!)
|
||||
(declare is-text?)
|
||||
|
||||
(defn assign-editor
|
||||
[editor]
|
||||
(ptk/reify ::assign-editor
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :editor] editor)
|
||||
(update-in [:workspace-local :editor-n] (fnil inc 0))))))
|
||||
|
||||
;; --- Helpers
|
||||
|
||||
(defn set-nodes!
|
||||
([editor props]
|
||||
(set-nodes! editor props #js {}))
|
||||
([editor props options]
|
||||
(.setNodes Transforms editor props options)
|
||||
editor))
|
||||
|
||||
(defn is-text?
|
||||
[v]
|
||||
(.isText Text v))
|
||||
|
||||
(defn is-paragraph?
|
||||
[v]
|
||||
(= (.-type v) "paragraph"))
|
||||
|
||||
;; --- Predicates
|
||||
|
||||
(defn enabled?
|
||||
[editor universal? pred]
|
||||
(when editor
|
||||
(let [result (.nodes Editor editor #js {:match pred :universal universal?})
|
||||
match (first (es6-iterator-seq result))]
|
||||
(array? match))))
|
||||
|
||||
(defn text-decoration-enabled?
|
||||
[editor type]
|
||||
(enabled? editor true
|
||||
(fn [v]
|
||||
(let [val (unchecked-get v "textDecoration")]
|
||||
(identical? type val)))))
|
||||
|
||||
(defn text-transform-enabled?
|
||||
[editor type]
|
||||
(enabled? editor true
|
||||
(fn [v]
|
||||
(let [val (unchecked-get v "textTransform")]
|
||||
(identical? type val)))))
|
||||
|
||||
(defn text-align-enabled?
|
||||
[editor type]
|
||||
(enabled? editor false
|
||||
(fn [v]
|
||||
(let [val (unchecked-get v "textAlign")]
|
||||
(identical? type val)))))
|
||||
|
||||
(defn vertical-align-enabled?
|
||||
[editor type]
|
||||
(enabled? editor false
|
||||
(fn [v]
|
||||
(let [val (unchecked-get v "verticalAlign")]
|
||||
(identical? type val)))))
|
||||
|
||||
;; --- Getters
|
||||
|
||||
(defn current-value
|
||||
[editor {:keys [universal?
|
||||
attr
|
||||
pred
|
||||
at]
|
||||
:as opts}]
|
||||
(when editor
|
||||
(let [options #js {:match pred
|
||||
:universal universal?}
|
||||
_ (when at (unchecked-set options "at" at))
|
||||
|
||||
result (.nodes Editor editor options)
|
||||
match (ffirst (es6-iterator-seq result))]
|
||||
(when (object? match)
|
||||
(unchecked-get match attr)))))
|
||||
|
||||
(defn current-line-height
|
||||
[editor {:keys [at default]}]
|
||||
(or (current-value editor {:at at
|
||||
:pred is-paragraph?
|
||||
:attr "lineHeight"
|
||||
:universal? false})
|
||||
default))
|
||||
|
||||
(defn current-letter-spacing
|
||||
[editor {:keys [at default]}]
|
||||
(or (current-value editor {:at at
|
||||
:pred is-text?
|
||||
:attr "letterSpacing"
|
||||
:universal? true})
|
||||
default))
|
||||
|
||||
|
||||
(defn current-font-family
|
||||
[editor {:keys [at default]}]
|
||||
(or (current-value editor {:at at
|
||||
:pred is-text?
|
||||
:attr "fontId"
|
||||
:universal? true})
|
||||
default))
|
||||
|
||||
(defn current-font-size
|
||||
[editor {:keys [at default]}]
|
||||
(or (current-value editor {:at at
|
||||
:pred is-text?
|
||||
:attr "fontSize"
|
||||
:universal? true})
|
||||
default))
|
||||
|
||||
|
||||
(defn current-font-variant
|
||||
[editor {:keys [at default]}]
|
||||
(or (current-value editor {:at at
|
||||
:pred is-text?
|
||||
:attr "fontVariantId"
|
||||
:universal? true})
|
||||
default))
|
||||
|
||||
|
||||
;; --- Setters
|
||||
|
||||
|
||||
(defn set-text-decoration!
|
||||
[editor type]
|
||||
(set-nodes! editor
|
||||
#js {:textDecoration type}
|
||||
#js {:match is-text?
|
||||
:split true}))
|
||||
|
||||
(defn set-text-align!
|
||||
[editor type]
|
||||
(set-nodes! editor
|
||||
#js {:textAlign type}
|
||||
#js {:match is-paragraph?}))
|
||||
|
||||
(defn set-text-transform!
|
||||
[editor type]
|
||||
(set-nodes! editor
|
||||
#js {:textTransform type}
|
||||
#js {:match is-text?
|
||||
:split true}))
|
||||
|
||||
(defn set-vertical-align!
|
||||
[editor type]
|
||||
(set-nodes! editor
|
||||
#js {:verticalAlign type}
|
||||
#js {:match (fn [item]
|
||||
(= "text-box" (unchecked-get item "type")))}))
|
||||
|
||||
(defn set-line-height!
|
||||
[editor val at]
|
||||
(set-nodes! editor
|
||||
#js {:lineHeight val}
|
||||
#js {:at at
|
||||
:match is-paragraph?}))
|
||||
|
||||
(defn set-letter-spacing!
|
||||
[editor val at]
|
||||
(set-nodes! editor
|
||||
#js {:letterSpacing val}
|
||||
#js {:at at
|
||||
:match is-text?
|
||||
:split true}))
|
||||
|
||||
(defn set-font!
|
||||
[editor id family]
|
||||
(set-nodes! editor
|
||||
#js {:fontId id
|
||||
:fontFamily family}
|
||||
#js {:match is-text?
|
||||
:split true}))
|
||||
|
||||
(defn set-font-size!
|
||||
[editor val]
|
||||
(set-nodes! editor
|
||||
#js {:fontSize val}
|
||||
#js {:match is-text?
|
||||
:split true}))
|
||||
|
||||
(defn set-font-variant!
|
||||
[editor id weight style]
|
||||
(set-nodes! editor
|
||||
#js {:fontVariantId id
|
||||
:fontWeight weight
|
||||
:fontStyle style}
|
||||
#js {:match is-text?
|
||||
:split true}))
|
||||
|
||||
|
||||
|
178
frontend/src/uxbox/main/fonts.cljs
Normal file
178
frontend/src/uxbox/main/fonts.cljs
Normal file
|
@ -0,0 +1,178 @@
|
|||
;; 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 uxbox.main.fonts
|
||||
"Fonts management and loading logic."
|
||||
(:require
|
||||
[beicon.core :as rx]
|
||||
[okulary.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.http :as http]
|
||||
[uxbox.util.transit :as t]
|
||||
[uxbox.util.timers :as ts]
|
||||
[uxbox.common.data :as d]
|
||||
[clojure.set :as set]))
|
||||
|
||||
(def local-fonts
|
||||
[{:id "sourcesanspro"
|
||||
:name "Source Sans Pro"
|
||||
:family "sourcesanspro"
|
||||
:variants [{:name "100" :weight "100" :style "normal"}
|
||||
{:name "100italic" :weight "100" :style "italic"}
|
||||
{:name "200" :weight "200" :style "normal"}
|
||||
{:name "200italic" :weight "200" :style "italic"}
|
||||
{:name "300" :weight "300" :style "normal"}
|
||||
{:name "300italic" :weight "300" :style "italic"}
|
||||
{:name "regular" :weight "400" :style "normal"}
|
||||
{:name "italic" :weight "400" :style "italic"}
|
||||
{:name "500" :weight "500" :style "normal"}
|
||||
{:name "500italic" :weight "500" :style "italic"}
|
||||
{:name "bold" :weight "bold" :style "normal"}
|
||||
{:name "bolditalic" :weight "bold" :style "italic"}
|
||||
{:name "black" :weight "900" :style "normal"}
|
||||
{:name "blackitalic" :weight "900" :style "italic"}]}
|
||||
{:id "roboto"
|
||||
:family "roboto"
|
||||
:name "Roboto"
|
||||
:variants [{:name "100" :weight "100" :style "normal"}
|
||||
{:name "100italic" :weight "100" :style "italic"}
|
||||
{:name "200" :weight "200" :style "normal"}
|
||||
{:name "200italic" :weight "200" :style "italic"}
|
||||
{:name "regular" :weight "400" :style "normal"}
|
||||
{:name "italic" :weight "400" :style "italic"}
|
||||
{:name "500" :weight "500" :style "normal"}
|
||||
{:name "500italic" :weight "500" :style "italic"}
|
||||
{:name "bold" :weight "bold" :style "normal"}
|
||||
{:name "bolditalic" :weight "bold" :style "italic"}
|
||||
{:name "black" :weight "900" :style "normal"}
|
||||
{:name "blackitalic" :weight "900" :style "italic"}]}
|
||||
{:id "robotocondensed"
|
||||
:family "robotocondensed"
|
||||
:name "Roboto Condensed"
|
||||
:variants [{:name "100" :weight "100" :style "normal"}
|
||||
{:name "100italic" :weight "100" :style "italic"}
|
||||
{:name "200" :weight "200" :style "normal"}
|
||||
{:name "200italic" :weight "200" :style "italic"}
|
||||
{:name "regular" :weight "400" :style "normal"}
|
||||
{:name "italic" :weight "400" :style "italic"}
|
||||
{:name "500" :weight "500" :style "normal"}
|
||||
{:name "500italic" :weight "500" :style "italic"}
|
||||
{:name "bold" :weight "bold" :style "normal"}
|
||||
{:name "bolditalic" :weight "bold" :style "italic"}
|
||||
{:name "black" :weight "900" :style "normal"}
|
||||
{:name "blackitalic" :weight "900" :style "italic"}]}])
|
||||
|
||||
(defonce fontsdb (l/atom {}))
|
||||
(defonce fontsview (l/atom {}))
|
||||
|
||||
(defn- materialize-fontsview
|
||||
[db]
|
||||
(reset! fontsview (reduce-kv (fn [acc k v]
|
||||
(assoc acc k (sort-by :name v)))
|
||||
{}
|
||||
(group-by :backend (vals db)))))
|
||||
(add-watch fontsdb "main"
|
||||
(fn [_ _ _ db]
|
||||
(ts/schedule-on-idle #(materialize-fontsview db))))
|
||||
|
||||
(defn register!
|
||||
[backend fonts]
|
||||
(let [fonts (map #(assoc % :backend backend) fonts)]
|
||||
(swap! fontsdb #(merge % (d/index-by :id fonts)))))
|
||||
|
||||
(register! :builtin local-fonts)
|
||||
|
||||
(defn resolve-variants
|
||||
[id]
|
||||
(get-in @fontsdb [id :variants]))
|
||||
|
||||
(defn resolve-fonts
|
||||
[backend]
|
||||
(get @fontsview backend))
|
||||
|
||||
;; --- Google Fonts Fetcher
|
||||
|
||||
(def ^:private gfonts-uri "/fonts/gfonts.2020.04.23.json")
|
||||
|
||||
(defn- parse-gfont-variant
|
||||
[variant]
|
||||
(cond
|
||||
(= "regular" variant)
|
||||
{:name "regular" :width "400" :style "normal"}
|
||||
|
||||
(= "italic" variant)
|
||||
{:name "italic" :width "400" :style "italic"}
|
||||
|
||||
:else
|
||||
(when-let [[a b c] (re-find #"^(\d+)(.*)$" variant)]
|
||||
(if (str/empty? c)
|
||||
{:name a :width b :style "normal"}
|
||||
{:name a :width b :style c}))))
|
||||
|
||||
(defn- parse-gfont
|
||||
[font]
|
||||
(let [family (get font "family")
|
||||
variants (get font "variants")]
|
||||
{:id (str "gfont-" (str/slug family))
|
||||
:family family
|
||||
:name family
|
||||
:variants (into [] (comp (map parse-gfont-variant)
|
||||
(filter identity))
|
||||
variants)}))
|
||||
|
||||
(let [url (unchecked-get js/location "origin")
|
||||
url (str url gfonts-uri)
|
||||
stm (->> (http/send! {:method :get :url url})
|
||||
(rx/map :body)
|
||||
(rx/map t/decode)
|
||||
(rx/mapcat #(get % "items"))
|
||||
(rx/map parse-gfont)
|
||||
(rx/reduce conj []))]
|
||||
(rx/subscribe stm #(register! :google %)))
|
||||
|
||||
;; --- Fonts Loader
|
||||
|
||||
(defonce loaded (l/atom #{}))
|
||||
|
||||
(defn- create-link-node
|
||||
[uri]
|
||||
(let [node (.createElement js/document "link")]
|
||||
(unchecked-set node "href" uri)
|
||||
(unchecked-set node "rel" "stylesheet")
|
||||
(unchecked-set node "type" "text/css")
|
||||
node))
|
||||
|
||||
(defmulti ^:private load-font :backend)
|
||||
|
||||
(defmethod load-font :builtin
|
||||
[{:keys [id] :as font}]
|
||||
(js/console.log "[debug:fonts]: loading builtin font" id))
|
||||
|
||||
(defmethod load-font :google
|
||||
[{:keys [id family variants] :as font}]
|
||||
(js/console.log "[debug:fonts]: loading google font" id)
|
||||
(let [base (str "https://fonts.googleapis.com/css?family=" family)
|
||||
variants (str/join "," (map :name variants))
|
||||
uri (str base ":" variants)
|
||||
node (create-link-node uri)]
|
||||
(.append (.-head js/document) node)
|
||||
nil))
|
||||
|
||||
(defmethod load-font :default
|
||||
[{:keys [backend] :as font}]
|
||||
(js/console.warn "no implementation found for" backend))
|
||||
|
||||
(defn ensure-loaded!
|
||||
[id]
|
||||
(when-not (contains? @loaded id)
|
||||
(when-let [font (get @fontsdb id)]
|
||||
(load-font font)
|
||||
(swap! loaded conj id))))
|
||||
|
|
@ -11,14 +11,22 @@
|
|||
[goog.object :as gobj]
|
||||
[lentes.core :as l]
|
||||
[rumext.alpha :as mf]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.util.geom.shapes :as geom]
|
||||
[uxbox.main.data.workspace.texts :as dwt]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.shapes.common :as common]
|
||||
[uxbox.main.ui.keyboard :as kbd]
|
||||
[uxbox.main.fonts :as fonts]
|
||||
[uxbox.util.color :as color]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.matrix :as gmt]))
|
||||
[uxbox.util.geom.shapes :as geom]
|
||||
[uxbox.util.geom.matrix :as gmt]
|
||||
["slate" :as slate]
|
||||
["slate-react" :as rslate])
|
||||
(:import goog.events.EventType
|
||||
goog.events.KeyCodes))
|
||||
|
||||
;; --- Events
|
||||
|
||||
|
@ -54,111 +62,267 @@
|
|||
(dom/prevent-default event)
|
||||
(when selected?
|
||||
(st/emit! (dw/start-edition-mode (:id shape)))))]
|
||||
|
||||
[:g.shape {:on-double-click on-double-click
|
||||
:on-mouse-down on-mouse-down
|
||||
:on-context-menu on-context-menu}
|
||||
(if edition?
|
||||
[:& text-shape-edit {:shape (geom/transform-shape frame shape)}]
|
||||
[:& text-shape {:shape (geom/transform-shape frame shape)}])]))
|
||||
[:& text-shape {:shape (geom/transform-shape frame shape)
|
||||
:selected? selected?}])]))
|
||||
|
||||
;; --- Text Styles Helpers
|
||||
;; --- Text Rendering
|
||||
|
||||
(defn- make-style
|
||||
[{:keys [fill-color
|
||||
fill-opacity
|
||||
font-family
|
||||
font-weight
|
||||
font-style
|
||||
font-size
|
||||
text-align
|
||||
line-height
|
||||
letter-spacing
|
||||
user-select
|
||||
width
|
||||
height]
|
||||
:or {fill-color "#000000"
|
||||
fill-opacity 1
|
||||
font-family "sourcesanspro"
|
||||
font-weight "normal"
|
||||
font-style "normal"
|
||||
font-size 18
|
||||
line-height 1.4
|
||||
letter-spacing 1
|
||||
text-align "left"
|
||||
user-select false}
|
||||
:as shape}]
|
||||
(let [color (-> fill-color
|
||||
(color/hex->rgba fill-opacity)
|
||||
(color/rgb->str))]
|
||||
(rumext.util/map->obj
|
||||
(merge
|
||||
{:fontSize (str font-size "px")
|
||||
:color color
|
||||
:width width
|
||||
:height height
|
||||
:whiteSpace "pre-wrap"
|
||||
:textAlign text-align
|
||||
:fontFamily font-family
|
||||
:fontWeight font-weight
|
||||
:fontStyle font-style
|
||||
:margin "0px"
|
||||
:padding "0px"
|
||||
:border "0px"
|
||||
:resize "none"
|
||||
:background "transparent"}
|
||||
(when user-select {:userSelect "auto"})
|
||||
(when line-height {:lineHeight line-height})
|
||||
(when letter-spacing {:letterSpacing letter-spacing})))))
|
||||
(defn obj-assoc!
|
||||
[obj attr value]
|
||||
(unchecked-set obj attr value)
|
||||
obj)
|
||||
|
||||
(defn- generate-text-box-styles
|
||||
[data]
|
||||
(let [valign (unchecked-get data "verticalAlign")
|
||||
base #js {:height "100%"
|
||||
:width "100%"
|
||||
:display "flex"}]
|
||||
(cond-> base
|
||||
(= valign "top") (obj-assoc! "alignItems" "flex-start")
|
||||
(= valign "center") (obj-assoc! "alignItems" "center")
|
||||
(= valign "bottom") (obj-assoc! "alignItems" "flex-end"))))
|
||||
|
||||
(mf/defc rt-text-box
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[props]
|
||||
(let [attrs (unchecked-get props "attributes")
|
||||
childs (unchecked-get props "children")
|
||||
data (unchecked-get props "element")
|
||||
type (unchecked-get data "type")
|
||||
style (generate-text-box-styles data)
|
||||
attrs (obj-assoc! attrs "style" style)
|
||||
attrs (obj-assoc! attrs "className" type)]
|
||||
[:> :div attrs childs]))
|
||||
|
||||
(defn- generate-text-styles
|
||||
[data]
|
||||
(let [valign (unchecked-get data "verticalAlign")
|
||||
base #js {:display "inline-block"
|
||||
:width "100%"}]
|
||||
base))
|
||||
|
||||
(mf/defc rt-text
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[props]
|
||||
(let [attrs (unchecked-get props "attributes")
|
||||
childs (unchecked-get props "children")
|
||||
data (unchecked-get props "element")
|
||||
type (unchecked-get data "type")
|
||||
style (generate-text-styles data)
|
||||
attrs (obj-assoc! attrs "style" style)
|
||||
attrs (obj-assoc! attrs "className" type)]
|
||||
[:> :div attrs childs]))
|
||||
|
||||
(defn- generate-paragraph-styles
|
||||
[data]
|
||||
(let [base #js {:fontSize "14px"
|
||||
:margin "inherit"
|
||||
:lineHeight "1.2"}
|
||||
lh (unchecked-get data "lineHeight")
|
||||
ta (unchecked-get data "textAlign")]
|
||||
(cond-> base
|
||||
ta (obj-assoc! "textAlign" ta)
|
||||
lh (obj-assoc! "lineHeight" lh))))
|
||||
|
||||
(mf/defc rt-pharagraph
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[props]
|
||||
(let [attrs (unchecked-get props "attributes")
|
||||
childs (unchecked-get props "children")
|
||||
data (unchecked-get props "element")
|
||||
style (generate-paragraph-styles data)
|
||||
attrs (obj-assoc! attrs "style" style)]
|
||||
[:> :p attrs childs]))
|
||||
|
||||
(defn- generate-leaf-styles
|
||||
[data]
|
||||
(let [letter-spacing (unchecked-get data "letterSpacing")
|
||||
text-decoration (unchecked-get data "textDecoration")
|
||||
text-transform (unchecked-get data "textTransform")
|
||||
|
||||
font-id (unchecked-get data "fontId")
|
||||
font-variant-id (unchecked-get data "fontVariantId")
|
||||
|
||||
font-family (unchecked-get data "fontFamily")
|
||||
font-size (unchecked-get data "fontSize")
|
||||
|
||||
fontsdb (mf/deref fonts/fontsdb)
|
||||
|
||||
base #js {:textDecoration text-decoration
|
||||
:textTransform text-transform}]
|
||||
|
||||
(when (and (string? letter-spacing)
|
||||
(pos? (alength letter-spacing)))
|
||||
(obj-assoc! base "letterSpacing" (str letter-spacing "px")))
|
||||
|
||||
(when (and (string? font-size)
|
||||
(pos? (alength font-size)))
|
||||
(obj-assoc! base "fontSize" (str font-size "px")))
|
||||
|
||||
(when (and (string? font-id)
|
||||
(pos? (alength font-id)))
|
||||
(let [font (get fontsdb font-id)]
|
||||
(fonts/ensure-loaded! font-id)
|
||||
(let [font-family (or (:family font)
|
||||
(unchecked-get data "fontFamily"))
|
||||
font-variant (d/seek #(= font-variant-id (:name %))
|
||||
(:variants font))
|
||||
font-style (or (:style font-variant)
|
||||
(unchecked-get data "fontStyle"))
|
||||
font-weight (or (:weight font-variant)
|
||||
(unchecked-get data "fontWeight"))]
|
||||
(obj-assoc! base "fontFamily" font-family)
|
||||
(obj-assoc! base "fontStyle" font-style)
|
||||
(obj-assoc! base "fontWeight" font-weight))))
|
||||
|
||||
base))
|
||||
|
||||
(mf/defc rt-leaf
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[props]
|
||||
(let [attrs (unchecked-get props "attributes")
|
||||
childs (unchecked-get props "children")
|
||||
data (unchecked-get props "leaf")
|
||||
style (generate-leaf-styles data)
|
||||
attrs (obj-assoc! attrs "style" style)]
|
||||
[:> :span attrs childs]))
|
||||
|
||||
(defn- render-element
|
||||
[props]
|
||||
(mf/html
|
||||
(let [element (unchecked-get props "element")]
|
||||
(case (unchecked-get element "type")
|
||||
"text-box" [:> rt-text-box props]
|
||||
"text" [:> rt-text props]
|
||||
"paragraph" [:> rt-pharagraph props]
|
||||
nil))))
|
||||
|
||||
(defn- render-leaf
|
||||
[props]
|
||||
(mf/html
|
||||
[:> rt-leaf props]))
|
||||
|
||||
;; --- Text Shape Edit
|
||||
|
||||
(defn- initial-text
|
||||
([] (initial-text ""))
|
||||
([text]
|
||||
#js [#js {:type "text-box"
|
||||
:children #js [#js {:type "text"
|
||||
:children #js [#js {:type "paragraph"
|
||||
:children #js [#js {:text text}]}]}]}]))
|
||||
(defn- parse-content
|
||||
[content]
|
||||
(cond
|
||||
(string? content) (initial-text content)
|
||||
(vector? content) (clj->js content)
|
||||
(object? content) content
|
||||
:else (initial-text)))
|
||||
|
||||
(mf/defc text-shape-edit
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [shape] :as props}]
|
||||
(let [ref (mf/use-ref)
|
||||
{:keys [id x y width height content]} shape
|
||||
(let [{:keys [id x y width height content]} shape
|
||||
|
||||
on-unmount
|
||||
state (mf/use-state #(parse-content content))
|
||||
value (mf/use-var @state)
|
||||
|
||||
editor (mf/use-memo #(rslate/withReact (slate/createEditor)))
|
||||
self-ref (mf/use-ref)
|
||||
|
||||
on-close
|
||||
(fn []
|
||||
(let [content (-> (mf/ref-val ref)
|
||||
(dom/get-value))]
|
||||
(st/emit! (dw/update-shape id {:content content}))))
|
||||
|
||||
|
||||
on-blur
|
||||
(fn [event]
|
||||
(st/emit! dw/clear-edition-mode
|
||||
dw/deselect-all))]
|
||||
(mf/use-effect
|
||||
#(let [dom (mf/ref-val ref)
|
||||
val (dom/get-value dom)]
|
||||
(.focus dom)
|
||||
(gobj/set dom "selectionStart" (count val))
|
||||
(gobj/set dom "selectionEnd" (count val))
|
||||
dw/deselect-all))
|
||||
|
||||
on-click
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(let [sidebar (dom/get-element "settings-bar")
|
||||
self (mf/ref-val self-ref)
|
||||
target (dom/get-target event)]
|
||||
(when-not (or (.contains sidebar target)
|
||||
(.contains self target))
|
||||
(on-close))))
|
||||
|
||||
on-keyup
|
||||
(fn [event]
|
||||
(when (= (.-keyCode event) 27) ; ESC
|
||||
(on-close)))
|
||||
|
||||
on-mount
|
||||
(fn []
|
||||
(let [content (-> (mf/ref-val ref)
|
||||
(dom/get-value))]
|
||||
(st/emit! (dw/update-shape id {:content content}))))))
|
||||
(let [
|
||||
lkey1 (events/listen js/document EventType.CLICK on-click)
|
||||
lkey2 (events/listen js/document EventType.KEYUP on-keyup)]
|
||||
(st/emit! (dwt/assign-editor editor))
|
||||
#(let [content (js->clj @value)]
|
||||
(st/emit! (dwt/assign-editor nil)
|
||||
(dw/update-shape id {:content content}))
|
||||
(events/unlistenByKey lkey1)
|
||||
(events/unlistenByKey lkey2))))
|
||||
|
||||
on-change
|
||||
(mf/use-callback
|
||||
(fn [val]
|
||||
(st/emit! (dwt/assign-editor editor))
|
||||
(reset! state val)
|
||||
(reset! value val)))]
|
||||
|
||||
(mf/use-effect on-mount)
|
||||
|
||||
[:foreignObject {:x x :y y :width width :height height}
|
||||
[:textarea {:style (make-style shape)
|
||||
:on-blur on-blur
|
||||
:default-value content
|
||||
:ref ref}]]))
|
||||
[:foreignObject {:x x :y y :width width :height height :ref self-ref}
|
||||
[:> rslate/Slate {:editor editor
|
||||
:value @state
|
||||
:on-change on-change}
|
||||
[:> rslate/Editable
|
||||
{:auto-focus "true"
|
||||
:spell-check "false"
|
||||
:class "rich-text"
|
||||
:render-element render-element
|
||||
:render-leaf render-leaf
|
||||
:on-blur (fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
;; WARN: monky patch
|
||||
(unchecked-set slate/Transforms "deselect" (constantly nil)))
|
||||
:placeholder "Type some text here..."}]]]))
|
||||
|
||||
;; --- Text Shape Wrapper
|
||||
|
||||
(mf/defc text-shape
|
||||
[{:keys [shape] :as props}]
|
||||
[{:keys [shape selected?] :as props}]
|
||||
(let [{:keys [id x y width height rotation content]} shape
|
||||
|
||||
transform (when (and rotation (pos? rotation))
|
||||
(str/format "rotate(%s %s %s)"
|
||||
rotation
|
||||
(+ x (/ width 2))
|
||||
(+ y (/ height 2))))]
|
||||
(+ y (/ height 2))))
|
||||
|
||||
content (parse-content content)
|
||||
editor (mf/use-memo #(rslate/withReact (slate/createEditor)))
|
||||
|
||||
on-mount
|
||||
(fn []
|
||||
(when selected?
|
||||
(st/emit! (dwt/assign-editor editor))
|
||||
#(st/emit! (dwt/assign-editor nil))))
|
||||
|
||||
render-element (mf/use-callback render-element)
|
||||
render-leaf (mf/use-callback render-leaf)]
|
||||
|
||||
(mf/use-effect (mf/deps id selected?) on-mount)
|
||||
|
||||
[:foreignObject {:x x
|
||||
:y y
|
||||
|
@ -166,4 +330,12 @@
|
|||
:id (str id)
|
||||
:width width
|
||||
:height height}
|
||||
[:div {:style (make-style shape)} content]]))
|
||||
|
||||
[:> rslate/Slate {:editor editor
|
||||
:value content}
|
||||
[:> rslate/Editable {:auto-focus "false"
|
||||
:read-only "true"
|
||||
:class "rich-text"
|
||||
:render-element render-element
|
||||
:render-leaf render-leaf
|
||||
:placeholder "Type some text here..."}]]]))
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
:segments []}
|
||||
{:type :text
|
||||
:name "Text"
|
||||
:content "Type your text here"}])
|
||||
:content nil}])
|
||||
|
||||
(defn start-drawing
|
||||
[type]
|
||||
|
|
|
@ -10,415 +10,334 @@
|
|||
(ns uxbox.main.ui.workspace.sidebar.options.text
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[okulary.core :as l]
|
||||
[uxbox.builtins.icons :as i]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.data.workspace.texts :as dwt]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.ui.workspace.sidebar.options.measures :refer [measures-menu]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.main.fonts :as fonts]
|
||||
[uxbox.util.math :as math]
|
||||
[uxbox.util.i18n :refer [tr]]))
|
||||
[uxbox.util.i18n :as i18n :refer [tr t]]
|
||||
["slate" :refer [Transforms]]))
|
||||
|
||||
(declare +fonts+)
|
||||
(def ^:private editor-ref
|
||||
(l/derived :editor refs/workspace-local))
|
||||
|
||||
(mf/defc fonts-menu
|
||||
[{:keys [shape] :as props}]
|
||||
(let [id (:id shape)
|
||||
font-family (:font-family shape "sourcesanspro")
|
||||
font-style (:font-style shape "normal")
|
||||
font-weight (:font-weight shape "normal")
|
||||
font-size (:font-size shape 16)
|
||||
text-align (:text-align shape "left")
|
||||
line-height (:line-height shape 1.4)
|
||||
letter-spacing (:letter-spacing shape 1)
|
||||
(mf/defc font-select-optgroups
|
||||
{::mf/wrap [mf/memo]}
|
||||
[]
|
||||
[:*
|
||||
[:optgroup {:label "Local"}
|
||||
(for [font fonts/local-fonts]
|
||||
[:option {:value (:id font)
|
||||
:key (:id font)}
|
||||
(:name font)])]
|
||||
[:optgroup {:label "Google"}
|
||||
(for [font (fonts/resolve-fonts :google)]
|
||||
[:option {:value (:id font)
|
||||
:key (:id font)}
|
||||
(:name font)])]])
|
||||
|
||||
styles (:styles (d/seek #(= (:id %) font-family) +fonts+))
|
||||
(mf/defc font-options
|
||||
[{:keys [editor shape] :as props}]
|
||||
(let [selection (mf/use-ref)
|
||||
font-id (dwt/current-font-family editor {:default "sourcesanspro"})
|
||||
font-size (dwt/current-font-size editor {:default "14"})
|
||||
font-var (dwt/current-font-variant editor {:default "regular"})
|
||||
|
||||
fonts (mf/deref fonts/fontsdb)
|
||||
font (get fonts font-id)
|
||||
|
||||
on-font-family-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value))
|
||||
attrs {:font-family value
|
||||
:font-weight "normal"
|
||||
:font-style "normal"}]
|
||||
(st/emit! (dw/update-shape id attrs))))
|
||||
(let [id (-> (dom/get-target event)
|
||||
(dom/get-value))
|
||||
font (get fonts id)]
|
||||
(dwt/set-font! editor id (:family font))
|
||||
(when (not= id font-id)
|
||||
(dwt/set-font-variant! editor nil nil nil))))
|
||||
|
||||
on-font-size-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-integer 0))
|
||||
attrs {:font-size value}]
|
||||
(st/emit! (dw/update-shape id attrs))))
|
||||
(let [val (-> (dom/get-target event)
|
||||
(dom/get-value))]
|
||||
(dwt/set-font-size! editor val)))
|
||||
|
||||
on-font-letter-spacing-change
|
||||
on-font-variant-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-double 0))
|
||||
attrs {:letter-spacing value}]
|
||||
(st/emit! (dw/update-shape id attrs))))
|
||||
|
||||
on-font-line-height-change
|
||||
(fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/parse-double 0))
|
||||
attrs {:line-height value}]
|
||||
(st/emit! (dw/update-shape id attrs))))
|
||||
|
||||
on-font-align-change
|
||||
(fn [event value]
|
||||
(let [attrs {:text-align value}]
|
||||
(st/emit! (dw/update-shape id attrs))))
|
||||
|
||||
on-font-style-change
|
||||
(fn [event]
|
||||
(let [[weight style] (-> (dom/get-target event)
|
||||
(dom/get-value)
|
||||
(d/read-string))
|
||||
attrs {:font-style style
|
||||
:font-weight weight}]
|
||||
(st/emit! (dw/update-shape id attrs))))
|
||||
(let [id (-> (dom/get-target event)
|
||||
(dom/get-value))
|
||||
variant (d/seek #(= id (:name %)) (:variants font))]
|
||||
(dwt/set-font! editor (:id font) (:family font))
|
||||
(dwt/set-font-variant! editor id (:weight variant) (:style variant))))
|
||||
]
|
||||
[:*
|
||||
[:div.row-flex
|
||||
[:select.input-select {:value font-id
|
||||
:on-change on-font-family-change}
|
||||
[:& font-select-optgroups]]]
|
||||
|
||||
[:div.row-flex
|
||||
[:div.editable-select
|
||||
[:select.input-select {:value font-size
|
||||
:on-change on-font-size-change}
|
||||
[:option {:value "8"} "8"]
|
||||
[:option {:value "9"} "9"]
|
||||
[:option {:value "10"} "10"]
|
||||
[:option {:value "11"} "11"]
|
||||
[:option {:value "12"} "12"]
|
||||
[:option {:value "14"} "14"]
|
||||
[:option {:value "18"} "18"]
|
||||
[:option {:value "24"} "24"]
|
||||
[:option {:value "36"} "36"]
|
||||
[:option {:value "48"} "48"]
|
||||
[:option {:value "72"} "72"]]
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:max "200"
|
||||
:value font-size
|
||||
:on-change on-font-size-change
|
||||
}]]
|
||||
|
||||
[:select.input-select {:value font-var
|
||||
:on-change on-font-variant-change}
|
||||
(for [variant (:variants font)]
|
||||
[:option {:value (:name variant)
|
||||
:key (pr-str variant)}
|
||||
(:name variant)])]]]))
|
||||
|
||||
|
||||
(mf/defc text-align-options
|
||||
[{:keys [editor] :as props}]
|
||||
(let [on-text-align-change
|
||||
(fn [event type]
|
||||
(js/console.log (dwt/set-text-align! editor type)))]
|
||||
;; --- Align
|
||||
|
||||
[:div.row-flex.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align left"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-align-enabled? editor "left"))
|
||||
:on-click #(on-text-align-change % "left")}
|
||||
i/text-align-left]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align center"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-align-enabled? editor "center"))
|
||||
:on-click #(on-text-align-change % "center")}
|
||||
i/text-align-center]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align right"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-align-enabled? editor "right"))
|
||||
:on-click #(on-text-align-change % "right")}
|
||||
i/text-align-right]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Justify"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-align-enabled? editor "justify"))
|
||||
:on-click #(on-text-align-change % "justify")}
|
||||
i/text-align-justify]]))
|
||||
|
||||
(mf/defc spacing-options
|
||||
[{:keys [editor] :as props}]
|
||||
(let [selection (mf/use-ref)
|
||||
lh (dwt/current-line-height editor {:default "1.2"
|
||||
:at (mf/ref-val selection)})
|
||||
ls (dwt/current-letter-spacing editor {:default "0"
|
||||
:at (mf/ref-val selection)})]
|
||||
[:div.row-flex
|
||||
[:div.input-icon
|
||||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt "Line height"}
|
||||
i/line-height]
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
:step "0.1"
|
||||
:min "0"
|
||||
:max "200"
|
||||
:value lh
|
||||
:on-focus (fn [event]
|
||||
(js/console.log "line-height on-focus")
|
||||
;; (mf/set-ref-val! selection (.-selection editor))
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event))
|
||||
:on-blur (fn [event]
|
||||
;; (js/console.log "line-height on-blur")
|
||||
(mf/set-ref-val! selection nil)
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event))
|
||||
:on-change (fn [event]
|
||||
(let [val (-> (dom/get-target event)
|
||||
(dom/get-value))
|
||||
sel (mf/ref-val selection)]
|
||||
;; (js/console.log "line-height on-change" sel val)
|
||||
(dwt/set-line-height! editor val sel)))}]]
|
||||
[:div.input-icon
|
||||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt "Letter spacing"}
|
||||
i/letter-spacing]
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
:step "0.1"
|
||||
:min "0"
|
||||
:max "200"
|
||||
:value ls
|
||||
:on-focus (fn [event]
|
||||
;; (js/console.log "letter-spacing on-focus")
|
||||
(mf/set-ref-val! selection (.-selection editor))
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event))
|
||||
:on-blur (fn [event]
|
||||
;; (js/console.log "letter-spacing on-blur")
|
||||
(mf/set-ref-val! selection nil)
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event))
|
||||
:on-change (fn [event]
|
||||
(let [val (-> (dom/get-target event)
|
||||
(dom/get-value))
|
||||
sel (mf/ref-val selection)]
|
||||
;; (js/console.log "letter-spacing on-change" sel val)
|
||||
(dwt/set-letter-spacing! editor val sel)))}]]]))
|
||||
|
||||
(mf/defc box-sizing-options
|
||||
[{:keys [editor] :as props}]
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Auto height"}
|
||||
i/auto-height]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Auto width"}
|
||||
i/auto-width]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Fixed size"}
|
||||
i/auto-fix]])
|
||||
|
||||
(mf/defc vertical-align-options
|
||||
[{:keys [editor] :as props}]
|
||||
(let [on-vertical-align-change
|
||||
(fn [event type]
|
||||
(dwt/set-vertical-align! editor type))]
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align top"
|
||||
:class (dom/classnames
|
||||
:current (dwt/vertical-align-enabled? editor "top"))
|
||||
:on-click #(on-vertical-align-change % "top")}
|
||||
i/align-top]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align middle"
|
||||
:class (dom/classnames
|
||||
:current (dwt/vertical-align-enabled? editor "center"))
|
||||
:on-click #(on-vertical-align-change % "center")}
|
||||
i/align-middle]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align bottom"
|
||||
:class (dom/classnames
|
||||
:current (dwt/vertical-align-enabled? editor "bottom"))
|
||||
:on-click #(on-vertical-align-change % "bottom")}
|
||||
i/align-bottom]]))
|
||||
|
||||
(mf/defc text-decoration-options
|
||||
[{:keys [editor] :as props}]
|
||||
(let [on-decoration-change
|
||||
(fn [event type]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(if (dwt/text-decoration-enabled? editor type)
|
||||
(dwt/set-text-decoration! editor "none")
|
||||
(dwt/set-text-decoration! editor type)))]
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle "Decoration"]
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "None"
|
||||
:on-click #(on-decoration-change % "none")}
|
||||
i/minus]
|
||||
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Underline"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-decoration-enabled? editor "underline"))
|
||||
:on-click #(on-decoration-change % "underline")}
|
||||
i/underline]
|
||||
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Strikethrough"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-decoration-enabled? editor "line-through"))
|
||||
:on-click #(on-decoration-change % "line-through")}
|
||||
i/strikethrough]]]))
|
||||
|
||||
|
||||
(mf/defc text-transform-options
|
||||
[{:keys [editor] :as props}]
|
||||
(let [on-text-transform-change
|
||||
(fn [event type]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(if (dwt/text-transform-enabled? editor type)
|
||||
(dwt/set-text-transform! editor "none")
|
||||
(dwt/set-text-transform! editor type)))]
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle "Case"]
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "None"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-transform-enabled? editor "none"))
|
||||
:on-click #(on-text-transform-change % "none")}
|
||||
|
||||
i/minus]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Uppercase"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-transform-enabled? editor "uppercase"))
|
||||
:on-click #(on-text-transform-change % "uppercase")}
|
||||
|
||||
i/uppercase]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Lowercase"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-transform-enabled? editor "lowercase"))
|
||||
:on-click #(on-text-transform-change % "lowercase")}
|
||||
|
||||
i/lowercase]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Titlecase"
|
||||
:class (dom/classnames
|
||||
:current (dwt/text-transform-enabled? editor "capitalize"))
|
||||
:on-click #(on-text-transform-change % "capitalize")}
|
||||
i/titlecase]]]))
|
||||
|
||||
(mf/defc text-menu
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [shape] :as props}]
|
||||
(let [id (:id shape)
|
||||
editor (:editor (mf/deref refs/workspace-local))
|
||||
locale (i18n/use-locale)]
|
||||
|
||||
[:div.element-set
|
||||
[:div.element-set-title (tr "workspace.options.font-options")]
|
||||
[:div.element-set-title (t locale "workspace.options.font-options")]
|
||||
[:div.element-set-content
|
||||
[:div.row-flex
|
||||
[:select.input-select {:value font-family
|
||||
:on-change on-font-family-change}
|
||||
(for [font +fonts+]
|
||||
[:option {:value (:id font)
|
||||
:key (:id font)}
|
||||
(:name font)])]]
|
||||
[:& font-options {:editor editor}]
|
||||
[:& text-align-options {:editor editor}]
|
||||
[:& spacing-options {:editor editor}]
|
||||
|
||||
[:div.row-flex
|
||||
[:div.editable-select
|
||||
[:select.input-select {:value font-size
|
||||
:on-change on-font-size-change}
|
||||
[:option {:value "8"} "8"]
|
||||
[:option {:value "9"} "9"]
|
||||
[:option {:value "10"} "10"]
|
||||
[:option {:value "11"} "11"]
|
||||
[:option {:value "12"} "12"]
|
||||
[:option {:value "14"} "14"]
|
||||
[:option {:value "18"} "18"]
|
||||
[:option {:value "24"} "24"]
|
||||
[:option {:value "36"} "36"]
|
||||
[:option {:value "48"} "48"]
|
||||
[:option {:value "72"} "72"]]
|
||||
[:input.input-text {:type "number"
|
||||
:min "0"
|
||||
:max "200"
|
||||
:value (-> font-size
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))
|
||||
:on-change on-font-size-change}]]
|
||||
[:& vertical-align-options {:editor editor}]
|
||||
[:& box-sizing-options {:editor editor}]]
|
||||
|
||||
[:select.input-select {:value (pr-str [font-weight font-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)])]]
|
||||
|
||||
[:div.row-flex.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align left"
|
||||
:class (when (= text-align "left") "current")
|
||||
:on-click #(on-font-align-change % "left")}
|
||||
i/text-align-left]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align center"
|
||||
:class (when (= text-align "center") "current")
|
||||
:on-click #(on-font-align-change % "center")}
|
||||
i/text-align-center]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align right"
|
||||
:class (when (= text-align "right") "current")
|
||||
:on-click #(on-font-align-change % "right")}
|
||||
i/text-align-right]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Justify"
|
||||
:class (when (= text-align "justify") "current")
|
||||
:on-click #(on-font-align-change % "justify")}
|
||||
i/text-align-justify]]
|
||||
|
||||
[:div.row-flex
|
||||
[:div.input-icon
|
||||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt "Line height"}
|
||||
i/line-height]
|
||||
[:input.input-text {:type "number"
|
||||
:step "0.1"
|
||||
:min "0"
|
||||
:max "200"
|
||||
:value (-> line-height
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))
|
||||
:on-change on-font-line-height-change}]]
|
||||
[:div.input-icon
|
||||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt "Letter spacing"}
|
||||
i/letter-spacing]
|
||||
[:input.input-text {:type "number"
|
||||
:step "0.1"
|
||||
:min "0"
|
||||
:max "200"
|
||||
:value (-> letter-spacing
|
||||
(math/precision 2)
|
||||
(d/coalesce-str "0"))
|
||||
:on-change on-font-letter-spacing-change}]]]
|
||||
|
||||
[:div.row-flex
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align top"}
|
||||
i/align-top]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align middle"}
|
||||
i/align-middle]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Align bottom"}
|
||||
i/align-bottom]]
|
||||
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Auto height"}
|
||||
i/auto-height]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Auto width"}
|
||||
i/auto-width]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Fixed size"}
|
||||
i/auto-fix]]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle "Decoration"]
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "None"}
|
||||
i/minus]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Underline"}
|
||||
i/underline]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Strikethrough"}
|
||||
i/strikethrough]]]
|
||||
|
||||
[:div.row-flex
|
||||
[:span.element-set-subtitle "Case"]
|
||||
[:div.align-icons
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "None"}
|
||||
i/minus]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Uppercase"}
|
||||
i/uppercase]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Lowercase"}
|
||||
i/lowercase]
|
||||
[:span.tooltip.tooltip-bottom
|
||||
{:alt "Titlecase"}
|
||||
i/titlecase]]]
|
||||
]]))
|
||||
|
||||
(def +fonts+
|
||||
[{:id "sourcesanspro"
|
||||
:name "Source Sans Pro"
|
||||
:styles [{:name "Extra-Light"
|
||||
:weight "100"
|
||||
:style "normal"}
|
||||
{:name "Extra-Light Italic"
|
||||
:weight "100"
|
||||
:style "italic"}
|
||||
{:name "Light"
|
||||
:weight "200"
|
||||
:style "normal"}
|
||||
{:name "Light Italic"
|
||||
:weight "200"
|
||||
:style "italic"}
|
||||
{:name "Regular"
|
||||
:weight "normal"
|
||||
:style "normal"}
|
||||
{:name "Italic"
|
||||
:weight "normal"
|
||||
:style "italic"}
|
||||
{:name "Semi-Bold"
|
||||
:weight "500"
|
||||
:style "normal"}
|
||||
{:name "Semi-Bold Italic"
|
||||
:weight "500"
|
||||
:style "italic"}
|
||||
{:name "Bold"
|
||||
:weight "bold"
|
||||
:style "normal"}
|
||||
{:name "Bold Italic"
|
||||
:weight "bold"
|
||||
:style "italic"}
|
||||
{:name "Black"
|
||||
:weight "900"
|
||||
:style "normal"}
|
||||
{:name "Black Italic"
|
||||
:weight "900"
|
||||
:style "italic"}]}
|
||||
{:id "opensans"
|
||||
:name "Open Sans"
|
||||
:styles [{:name "Extra-Light"
|
||||
:weight "100"
|
||||
:style "normal"}
|
||||
{:name "Extra-Light Italic"
|
||||
:weight "100"
|
||||
:style "italic"}
|
||||
{:name "Light"
|
||||
:weight "200"
|
||||
:style "normal"}
|
||||
{:name "Light Italic"
|
||||
:weight "200"
|
||||
:style "italic"}
|
||||
{:name "Regular"
|
||||
:weight "normal"
|
||||
:style "normal"}
|
||||
{:name "Italic"
|
||||
:weight "normal"
|
||||
:style "italic"}
|
||||
{:name "Semi-Bold"
|
||||
:weight "500"
|
||||
:style "normal"}
|
||||
{:name "Semi-Bold Italic"
|
||||
:weight "500"
|
||||
:style "italic"}
|
||||
{:name "Bold"
|
||||
:weight "bold"
|
||||
:style "normal"}
|
||||
{:name "Bold Italic"
|
||||
:weight "bold"
|
||||
:style "italic"}
|
||||
{:name "Black"
|
||||
:weight "900"
|
||||
:style "normal"}
|
||||
{:name "Black Italic"
|
||||
:weight "900"
|
||||
:style "italic"}]}
|
||||
{:id "bebas"
|
||||
:name "Bebas"
|
||||
:styles [{:name "Normal"
|
||||
:weight "normal"
|
||||
:style "normal"}]}
|
||||
{:id "gooddog"
|
||||
:name "Good Dog"
|
||||
:styles [{:name "Normal"
|
||||
:weight "normal"
|
||||
:style "normal"}]}
|
||||
{:id "caviardreams"
|
||||
:name "Caviar Dreams"
|
||||
:styles [{:name "Normal"
|
||||
:weight "normal"
|
||||
:style "normal"}
|
||||
{:name "Normal Italic"
|
||||
:weight "normal"
|
||||
:style "italic"}
|
||||
{:name "Bold"
|
||||
:weight "bold"
|
||||
:style "normal"}
|
||||
{:name "Bold Italic"
|
||||
:weight "bold"
|
||||
:style "italic"}]}
|
||||
{:id "ptsans"
|
||||
:name "PT Sans"
|
||||
:styles [{:name "Normal"
|
||||
:weight "normal"
|
||||
:style "normal"}
|
||||
{:name "Normal Italic"
|
||||
:weight "normal"
|
||||
:style "italic"}
|
||||
{:name "Bold"
|
||||
:weight "bold"
|
||||
:style "normal"}
|
||||
{:name "Bold Italic"
|
||||
:weight "bold"
|
||||
:style "italic"}]}
|
||||
{:id "roboto"
|
||||
:name "Roboto"
|
||||
:styles [{:name "Extra-Light"
|
||||
:weight "100"
|
||||
:style "normal"}
|
||||
{:name "Extra-Light Italic"
|
||||
:weight "100"
|
||||
:style "italic"}
|
||||
{:name "Light"
|
||||
:weight "200"
|
||||
:style "normal"}
|
||||
{:name "Light Italic"
|
||||
:weight "200"
|
||||
:style "italic"}
|
||||
{:name "Regular"
|
||||
:weight "normal"
|
||||
:style "normal"}
|
||||
{:name "Italic"
|
||||
:weight "normal"
|
||||
:style "italic"}
|
||||
{:name "Semi-Bold"
|
||||
:weight "500"
|
||||
:style "normal"}
|
||||
{:name "Semi-Bold Italic"
|
||||
:weight "500"
|
||||
:style "italic"}
|
||||
{:name "Bold"
|
||||
:weight "bold"
|
||||
:style "normal"}
|
||||
{:name "Bold Italic"
|
||||
:weight "bold"
|
||||
:style "italic"}
|
||||
{:name "Black"
|
||||
:weight "900"
|
||||
:style "normal"}
|
||||
{:name "Black Italic"
|
||||
:weight "900"
|
||||
:style "italic"}]}
|
||||
{:id "robotocondensed"
|
||||
:name "Roboto Condensed"
|
||||
:styles [{:name "Extra-Light"
|
||||
:weight "100"
|
||||
:style "normal"}
|
||||
{:name "Extra-Light Italic"
|
||||
:weight "100"
|
||||
:style "italic"}
|
||||
{:name "Light"
|
||||
:weight "200"
|
||||
:style "normal"}
|
||||
{:name "Light Italic"
|
||||
:weight "200"
|
||||
:style "italic"}
|
||||
{:name "Regular"
|
||||
:weight "normal"
|
||||
:style "normal"}
|
||||
{:name "Italic"
|
||||
:weight "normal"
|
||||
:style "italic"}
|
||||
{:name "Semi-Bold"
|
||||
:weight "500"
|
||||
:style "normal"}
|
||||
{:name "Semi-Bold Italic"
|
||||
:weight "500"
|
||||
:style "italic"}
|
||||
{:name "Bold"
|
||||
:weight "bold"
|
||||
:style "normal"}
|
||||
{:name "Bold Italic"
|
||||
:weight "bold"
|
||||
:style "italic"}
|
||||
{:name "Black"
|
||||
:weight "900"
|
||||
:style "normal"}
|
||||
{:name "Black Italic"
|
||||
:weight "900"
|
||||
:style "italic"}]}
|
||||
])
|
||||
[:& text-decoration-options {:editor editor}]
|
||||
[:& text-transform-options {:editor editor}]]]))
|
||||
|
||||
(mf/defc options
|
||||
[{:keys [shape] :as props}]
|
||||
[:div
|
||||
[:& measures-menu {:shape shape}]
|
||||
[:& fonts-menu {:shape shape}]])
|
||||
[:& text-menu {:shape shape}]])
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue