mirror of
https://github.com/penpot/penpot.git
synced 2025-05-20 22:56:10 +02:00
commit
0eb2336bc6
24 changed files with 648 additions and 327 deletions
|
@ -29,11 +29,13 @@
|
|||
[app.main.ui.shapes.text :as text]
|
||||
[app.main.ui.shapes.text.fontfaces :as ff]
|
||||
[app.util.object :as obj]
|
||||
[app.util.strings :as ust]
|
||||
[app.util.timers :as ts]
|
||||
[cuerdas.core :as str]
|
||||
[debug :refer [debug?]]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(def ^:const viewbox-decimal-precision 3)
|
||||
(def ^:private default-color clr/canvas)
|
||||
|
||||
(mf/defc background
|
||||
|
@ -139,8 +141,13 @@
|
|||
;; Don't wrap svg elements inside a <g> otherwise some can break
|
||||
[:> svg-raw-wrapper {:shape shape :frame frame}]))))))
|
||||
|
||||
(defn get-viewbox [{:keys [x y width height] :or {x 0 y 0 width 100 height 100}}]
|
||||
(str/fmt "%s %s %s %s" x y width height))
|
||||
(defn format-viewbox
|
||||
"Format a viewbox given a rectangle"
|
||||
[{:keys [x y width height] :or {x 0 y 0 width 100 height 100}}]
|
||||
(str/join
|
||||
" "
|
||||
(->> [x y width height]
|
||||
(map #(ust/format-precision % viewbox-decimal-precision)))))
|
||||
|
||||
(mf/defc page-svg
|
||||
{::mf/wrap [mf/memo]}
|
||||
|
@ -160,7 +167,7 @@
|
|||
vport (when (and (some? width) (some? height))
|
||||
{:width width :height height})
|
||||
dim (calculate-dimensions data vport)
|
||||
vbox (get-viewbox dim)
|
||||
vbox (format-viewbox dim)
|
||||
background-color (get-in data [:options :background] default-color)
|
||||
frame-wrapper
|
||||
(mf/use-memo
|
||||
|
@ -221,15 +228,15 @@
|
|||
|
||||
width (* (:width frame) zoom)
|
||||
height (* (:height frame) zoom)
|
||||
vbox (str "0 0 " (:width frame 0)
|
||||
" " (:height frame 0))
|
||||
vbox (format-viewbox {:width (:width frame 0) :height (:height frame 0)})
|
||||
|
||||
wrapper (mf/use-memo
|
||||
(mf/deps objects)
|
||||
#(frame-wrapper-factory objects))]
|
||||
|
||||
[:svg {:view-box vbox
|
||||
:width width
|
||||
:height height
|
||||
:width (ust/format-precision width viewbox-decimal-precision)
|
||||
:height (ust/format-precision height viewbox-decimal-precision)
|
||||
:version "1.1"
|
||||
:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
|
@ -255,18 +262,20 @@
|
|||
|
||||
group (get objects group-id)
|
||||
|
||||
|
||||
width (* (:width group) zoom)
|
||||
height (* (:height group) zoom)
|
||||
vbox (str "0 0 " (:width group 0)
|
||||
" " (:height group 0))
|
||||
vbox (format-viewbox {:width (:width group 0)
|
||||
:height (:height group 0)})
|
||||
|
||||
group-wrapper
|
||||
(mf/use-memo
|
||||
(mf/deps objects)
|
||||
#(group-wrapper-factory objects))]
|
||||
|
||||
[:svg {:view-box vbox
|
||||
:width width
|
||||
:height height
|
||||
:width (ust/format-precision width viewbox-decimal-precision)
|
||||
:height (ust/format-precision height viewbox-decimal-precision)
|
||||
:version "1.1"
|
||||
:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
|
@ -281,7 +290,7 @@
|
|||
root (get objects id)
|
||||
|
||||
{:keys [width height]} (:selrect root)
|
||||
vbox (str "0 0 " width " " height)
|
||||
vbox (format-viewbox {:width width :height height})
|
||||
|
||||
modifier (-> (gpt/point (:x root) (:y root))
|
||||
(gpt/negate)
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.simple-math :as sm]
|
||||
[app.util.strings :as ust]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn num? [val]
|
||||
|
@ -24,13 +25,16 @@
|
|||
{::mf/wrap-props false
|
||||
::mf/forward-ref true}
|
||||
[props external-ref]
|
||||
(let [value-str (obj/get props "value")
|
||||
min-val-str (obj/get props "min")
|
||||
max-val-str (obj/get props "max")
|
||||
wrap-value? (obj/get props "data-wrap")
|
||||
on-change (obj/get props "onChange")
|
||||
title (obj/get props "title")
|
||||
default-val (obj/get props "default" 0)
|
||||
(let [value-str (obj/get props "value")
|
||||
min-val-str (obj/get props "min")
|
||||
max-val-str (obj/get props "max")
|
||||
step-val-str (obj/get props "step")
|
||||
wrap-value? (obj/get props "data-wrap")
|
||||
on-change (obj/get props "onChange")
|
||||
on-blur (obj/get props "onBlur")
|
||||
title (obj/get props "title")
|
||||
default-val (obj/get props "default" 0)
|
||||
precision (obj/get props "precision")
|
||||
|
||||
;; We need a ref pointing to the input dom element, but the user
|
||||
;; of this component may provide one (that is forwarded here).
|
||||
|
@ -56,6 +60,15 @@
|
|||
(string? max-val-str)
|
||||
(d/parse-integer max-val-str))
|
||||
|
||||
step-val (cond
|
||||
(number? step-val-str)
|
||||
step-val-str
|
||||
|
||||
(string? step-val-str)
|
||||
(d/parse-integer step-val-str)
|
||||
|
||||
:else 1)
|
||||
|
||||
parse-value
|
||||
(mf/use-callback
|
||||
(mf/deps ref min-val max-val value)
|
||||
|
@ -65,7 +78,10 @@
|
|||
(sm/expr-eval value))]
|
||||
(when (num? new-value)
|
||||
(-> new-value
|
||||
(math/round)
|
||||
(cond-> (number? precision)
|
||||
(math/precision precision))
|
||||
(cond-> (nil? precision)
|
||||
(math/round))
|
||||
(cljs.core/max us/min-safe-int)
|
||||
(cljs.core/min us/max-safe-int)
|
||||
(cond->
|
||||
|
@ -80,7 +96,9 @@
|
|||
(mf/deps ref)
|
||||
(fn [new-value]
|
||||
(let [input-node (mf/ref-val ref)]
|
||||
(dom/set-value! input-node (str new-value)))))
|
||||
(dom/set-value! input-node (if (some? precision)
|
||||
(ust/format-precision new-value precision)
|
||||
(str new-value))))))
|
||||
|
||||
apply-value
|
||||
(mf/use-callback
|
||||
|
@ -97,18 +115,18 @@
|
|||
(let [current-value (parse-value)]
|
||||
(when current-value
|
||||
(let [increment (if (kbd/shift? event)
|
||||
(if up? 10 -10)
|
||||
(if up? 1 -1))
|
||||
(if up? (* step-val 10) (* step-val -10))
|
||||
(if up? step-val (- step-val)))
|
||||
|
||||
new-value (+ current-value increment)
|
||||
new-value (cond
|
||||
(and wrap-value? (num? max-val) (num? min-val)
|
||||
(> new-value max-val) up?)
|
||||
(-> new-value (- max-val) (+ min-val) (- 1))
|
||||
(-> new-value (- max-val) (+ min-val) (- step-val))
|
||||
|
||||
(and wrap-value? (num? min-val) (num? max-val)
|
||||
(< new-value min-val) down?)
|
||||
(-> new-value (- min-val) (+ max-val) (+ 1))
|
||||
(-> new-value (- min-val) (+ max-val) (+ step-val))
|
||||
|
||||
(and (num? min-val) (< new-value min-val))
|
||||
min-val
|
||||
|
@ -144,12 +162,13 @@
|
|||
|
||||
handle-blur
|
||||
(mf/use-callback
|
||||
(mf/deps parse-value apply-value update-input)
|
||||
(mf/deps parse-value apply-value update-input on-blur)
|
||||
(fn [_]
|
||||
(let [new-value (or (parse-value) default-val)]
|
||||
(if new-value
|
||||
(apply-value new-value)
|
||||
(update-input new-value)))))
|
||||
(update-input new-value)))
|
||||
(when on-blur (on-blur))))
|
||||
|
||||
props (-> props
|
||||
(obj/without ["value" "onChange"])
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
(filter #(= :ready (:status %)))
|
||||
(mapv #(assoc % :status :importing))))
|
||||
|
||||
(defn update-status [files file-id status progress]
|
||||
(defn update-status [files file-id status progress errors]
|
||||
(->> files
|
||||
(mapv (fn [file]
|
||||
(cond-> file
|
||||
|
@ -106,7 +106,10 @@
|
|||
(assoc :status status)
|
||||
|
||||
(and (= file-id (:file-id file)) (= status :import-progress))
|
||||
(assoc :progress progress))))))
|
||||
(assoc :progress progress)
|
||||
|
||||
(= file-id (:file-id file))
|
||||
(assoc :errors errors))))))
|
||||
|
||||
(defn parse-progress-message
|
||||
[message]
|
||||
|
@ -139,9 +142,10 @@
|
|||
|
||||
(let [loading? (or (= :analyzing (:status file))
|
||||
(= :importing (:status file)))
|
||||
load-success? (= :import-success (:status file))
|
||||
analyze-error? (= :analyze-error (:status file))
|
||||
import-finish? (= :import-finish (:status file))
|
||||
import-error? (= :import-error (:status file))
|
||||
import-warn? (d/not-empty? (:errors file))
|
||||
ready? (= :ready (:status file))
|
||||
is-shared? (:shared file)
|
||||
progress (:progress file)
|
||||
|
@ -177,7 +181,8 @@
|
|||
[:div.file-entry
|
||||
{:class (dom/classnames
|
||||
:loading loading?
|
||||
:success load-success?
|
||||
:success (and import-finish? (not import-warn?) (not import-error?))
|
||||
:warning (and import-finish? import-warn? (not import-error?))
|
||||
:error (or import-error? analyze-error?)
|
||||
:editable (and ready? (not editing?)))}
|
||||
|
||||
|
@ -185,8 +190,9 @@
|
|||
[:div.file-icon
|
||||
(cond loading? i/loader-pencil
|
||||
ready? i/logo-icon
|
||||
load-success? i/tick
|
||||
import-warn? i/msg-warning
|
||||
import-error? i/close
|
||||
import-finish? i/tick
|
||||
analyze-error? i/close)]
|
||||
|
||||
(if editing?
|
||||
|
@ -212,7 +218,7 @@
|
|||
[:div.error-message
|
||||
(tr "dashboard.import.import-error")]
|
||||
|
||||
(and (not load-success?) (some? progress))
|
||||
(and (not import-finish?) (some? progress))
|
||||
[:div.progress-message (parse-progress-message progress)])
|
||||
|
||||
[:div.linked-libraries
|
||||
|
@ -258,9 +264,8 @@
|
|||
:project-id project-id
|
||||
:files files})
|
||||
(rx/subs
|
||||
(fn [{:keys [file-id status message] :as msg}]
|
||||
(log/debug :msg msg)
|
||||
(swap! state update :files update-status file-id status message))))))
|
||||
(fn [{:keys [file-id status message errors] :as msg}]
|
||||
(swap! state update :files update-status file-id status message errors))))))
|
||||
|
||||
handle-cancel
|
||||
(mf/use-callback
|
||||
|
@ -291,7 +296,8 @@
|
|||
(st/emit! (modal/hide))
|
||||
(when on-finish-import (on-finish-import))))
|
||||
|
||||
success-files (->> @state :files (filter #(= (:status %) :import-success)) count)
|
||||
warning-files (->> @state :files (filter #(and (= (:status %) :import-finish) (d/not-empty? (:errors %)))) count)
|
||||
success-files (->> @state :files (filter #(and (= (:status %) :import-finish) (empty? (:errors %)))) count)
|
||||
pending-analysis? (> (->> @state :files (filter #(= (:status %) :analyzing)) count) 0)
|
||||
pending-import? (> (->> @state :files (filter #(= (:status %) :importing)) count) 0)]
|
||||
|
||||
|
@ -316,11 +322,15 @@
|
|||
{:on-click handle-cancel} i/close]]
|
||||
|
||||
[:div.modal-content
|
||||
(when (and (= :importing (:status @state))
|
||||
(not pending-import?))
|
||||
[:div.feedback-banner
|
||||
[:div.icon i/checkbox-checked]
|
||||
[:div.message (tr "dashboard.import.import-message" success-files)]])
|
||||
(when (and (= :importing (:status @state)) (not pending-import?))
|
||||
(if (> warning-files 0)
|
||||
[:div.feedback-banner.warning
|
||||
[:div.icon i/msg-warning]
|
||||
[:div.message (tr "dashboard.import.import-warning" warning-files success-files)]]
|
||||
|
||||
[:div.feedback-banner
|
||||
[:div.icon i/checkbox-checked]
|
||||
[:div.message (tr "dashboard.import.import-message" success-files)]]))
|
||||
|
||||
(for [file (->> (:files @state) (filterv (comp not :deleted?)))]
|
||||
(let [editing? (and (some? (:file-id file))
|
||||
|
|
|
@ -18,7 +18,8 @@
|
|||
[width style]
|
||||
(let [values (case style
|
||||
:mixed [5 5 1 5]
|
||||
:dotted [5 5]
|
||||
;; We want 0 so they are circles
|
||||
:dotted [(- width) 5]
|
||||
:dashed [10 10]
|
||||
nil)]
|
||||
|
||||
|
@ -132,9 +133,13 @@
|
|||
;; for inner or outer strokes.
|
||||
(and (spec/stroke-caps-line (:stroke-cap-start shape))
|
||||
(= (:stroke-cap-start shape) (:stroke-cap-end shape))
|
||||
(not (#{:inner :outer} (:stroke-alignment shape))))
|
||||
(not (#{:inner :outer} (:stroke-alignment shape)))
|
||||
(not= :dotted stroke-style))
|
||||
(assoc :strokeLinecap (:stroke-cap-start shape))
|
||||
|
||||
(= :dotted stroke-style)
|
||||
(assoc :strokeLinecap "round")
|
||||
|
||||
;; For other cap types we use markers.
|
||||
(and (or (spec/stroke-caps-marker (:stroke-cap-start shape))
|
||||
(and (spec/stroke-caps-line (:stroke-cap-start shape))
|
||||
|
|
|
@ -14,11 +14,10 @@
|
|||
[cuerdas.core :as str]))
|
||||
|
||||
(defn generate-root-styles
|
||||
[shape node]
|
||||
[_shape node]
|
||||
(let [valign (:vertical-align node "top")
|
||||
width (some-> (:width shape) (+ 1))
|
||||
base #js {:height (or (:height shape) "100%")
|
||||
:width (or width "100%")
|
||||
base #js {:height "100%"
|
||||
:width "100%"
|
||||
:fontFamily "sourcesanspro"}]
|
||||
(cond-> base
|
||||
(= valign "top") (obj/set! "justifyContent" "flex-start")
|
||||
|
|
|
@ -299,7 +299,7 @@
|
|||
[{:keys [file layout project page-id] :as props}]
|
||||
(let [team-id (:team-id project)
|
||||
zoom (mf/deref refs/selected-zoom)
|
||||
params {:page-id page-id :file-id (:id file)}
|
||||
params {:page-id page-id :file-id (:id file) :section "interactions"}
|
||||
|
||||
go-back
|
||||
(mf/use-callback
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
(ns app.main.ui.workspace.shapes.text.editor
|
||||
(:require
|
||||
["draft-js" :as draft]
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.text :as txt]
|
||||
[app.main.data.workspace :as dw]
|
||||
|
@ -72,20 +71,18 @@
|
|||
(def empty-editor-state
|
||||
(ted/create-editor-state nil default-decorator))
|
||||
|
||||
(defn get-content-changes
|
||||
[old-state state]
|
||||
(let [old-blocks (js->clj (.toJS (.getBlockMap (.getCurrentContent ^js old-state)))
|
||||
:keywordize-keys false)
|
||||
new-blocks (js->clj (.toJS (.getBlockMap (.getCurrentContent ^js state)))
|
||||
(defn get-blocks-to-setup [block-changes]
|
||||
(->> block-changes
|
||||
(filter (fn [[_ v]]
|
||||
(nil? (:old v))))
|
||||
(mapv first)))
|
||||
|
||||
:keywordize-keys false)]
|
||||
(->> old-blocks
|
||||
(d/mapm
|
||||
(fn [bkey bstate]
|
||||
{:old (get bstate "text")
|
||||
:new (get-in new-blocks [bkey "text"])}))
|
||||
(filter #(contains? new-blocks (first %)))
|
||||
(into {}))))
|
||||
(defn get-blocks-to-add-styles
|
||||
[block-changes]
|
||||
(->> block-changes
|
||||
(filter (fn [[_ v]]
|
||||
(and (not= (:old v) (:new v)) (= (:old v) ""))))
|
||||
(mapv first)))
|
||||
|
||||
(mf/defc text-shape-edit-html
|
||||
{::mf/wrap [mf/memo]
|
||||
|
@ -143,25 +140,35 @@
|
|||
(fn [state]
|
||||
(let [old-state (mf/ref-val prev-value)]
|
||||
(if (and (some? state) (some? old-state))
|
||||
(let [block-states (get-content-changes old-state state)
|
||||
|
||||
block-to-add-styles
|
||||
(->> block-states
|
||||
(filter
|
||||
(fn [[_ v]]
|
||||
(and (not= (:old v) (:new v))
|
||||
(= (:old v) ""))))
|
||||
(mapv first))]
|
||||
(ted/apply-block-styles-to-content state block-to-add-styles))
|
||||
(let [block-changes (ted/get-content-changes old-state state)
|
||||
prev-data (ted/get-editor-current-inline-styles old-state)
|
||||
block-to-setup (get-blocks-to-setup block-changes)
|
||||
block-to-add-styles (get-blocks-to-add-styles block-changes)]
|
||||
(-> state
|
||||
(ted/setup-block-styles block-to-setup prev-data)
|
||||
(ted/apply-block-styles-to-content block-to-add-styles)))
|
||||
state))))
|
||||
|
||||
on-change
|
||||
(mf/use-callback
|
||||
(fn [val]
|
||||
(let [val (handle-change val)
|
||||
val (if (true? @blurred)
|
||||
(ted/add-editor-blur-selection val)
|
||||
(ted/remove-editor-blur-selection val))]
|
||||
(let [prev-val (mf/ref-val prev-value)
|
||||
styleOverride (ted/get-style-override prev-val)
|
||||
|
||||
;; If the content and the selection are the same we keep the style override
|
||||
keep-style? (and (some? styleOverride)
|
||||
(ted/content-equals prev-val val)
|
||||
(ted/selection-equals prev-val val))
|
||||
|
||||
val (cond-> (handle-change val)
|
||||
@blurred
|
||||
(ted/add-editor-blur-selection)
|
||||
|
||||
(not @blurred)
|
||||
(ted/remove-editor-blur-selection)
|
||||
|
||||
keep-style?
|
||||
(ted/set-style-override styleOverride))]
|
||||
(st/emit! (dwt/update-editor-state shape val)))))
|
||||
|
||||
on-editor
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
[app.main.fonts :as fonts]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.editable-select :refer [editable-select]]
|
||||
[app.main.ui.components.numeric-input :refer [numeric-input]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
|
||||
[app.util.dom :as dom]
|
||||
|
@ -22,6 +23,7 @@
|
|||
[app.util.keyboard :as kbd]
|
||||
[app.util.object :as obj]
|
||||
[app.util.router :as rt]
|
||||
[app.util.strings :as ust]
|
||||
[app.util.timers :as tm]
|
||||
[cuerdas.core :as str]
|
||||
[goog.events :as events]
|
||||
|
@ -30,7 +32,7 @@
|
|||
(defn- attr->string [value]
|
||||
(if (= value :multiple)
|
||||
""
|
||||
(str value)))
|
||||
(ust/format-precision value 2)))
|
||||
|
||||
(defn- get-next-font
|
||||
[{:keys [id] :as current} fonts]
|
||||
|
@ -350,20 +352,19 @@
|
|||
letter-spacing (or letter-spacing "0")
|
||||
|
||||
handle-change
|
||||
(fn [event attr]
|
||||
(let [new-spacing (dom/get-target-val event)]
|
||||
(on-change {attr new-spacing})))]
|
||||
(fn [value attr]
|
||||
(on-change {attr (str value)}))]
|
||||
|
||||
[:div.spacing-options
|
||||
[:div.input-icon
|
||||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt (tr "workspace.options.text-options.line-height")}
|
||||
i/line-height]
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
:step "0.1"
|
||||
:min "-200"
|
||||
:max "200"
|
||||
[:> numeric-input
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:precision 2
|
||||
:value (attr->string line-height)
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change #(handle-change % :line-height)
|
||||
|
@ -373,11 +374,11 @@
|
|||
[:span.icon-before.tooltip.tooltip-bottom
|
||||
{:alt (tr "workspace.options.text-options.letter-spacing")}
|
||||
i/letter-spacing]
|
||||
[:input.input-text
|
||||
{:type "number"
|
||||
:step "0.1"
|
||||
:min "-200"
|
||||
:max "200"
|
||||
[:> numeric-input
|
||||
{:min -200
|
||||
:max 200
|
||||
:step 0.1
|
||||
:precision 2
|
||||
:value (attr->string letter-spacing)
|
||||
:placeholder (tr "settings.multiple")
|
||||
:on-change #(handle-change % :letter-spacing)
|
||||
|
|
|
@ -16,30 +16,54 @@
|
|||
(defn- text-corrected-transform
|
||||
"If we apply a scale directly to the texts it will show deformed so we need to create this
|
||||
correction matrix to \"undo\" the resize but keep the other transformations."
|
||||
[{:keys [points transform transform-inverse]} current-transform modifiers]
|
||||
[{:keys [x y width height points transform transform-inverse] :as shape} current-transform modifiers]
|
||||
|
||||
(let [corner-pt (first points)
|
||||
transform (or transform (gmt/matrix))
|
||||
transform-inverse (or transform-inverse (gmt/matrix))
|
||||
corner-pt (cond-> corner-pt (some? transform-inverse) (gpt/transform transform-inverse))
|
||||
|
||||
current-transform
|
||||
(if (some? (:resize-vector modifiers))
|
||||
(gmt/multiply
|
||||
current-transform
|
||||
transform
|
||||
(gmt/scale-matrix (gpt/inverse (:resize-vector modifiers)) (gpt/transform corner-pt transform-inverse))
|
||||
transform-inverse)
|
||||
current-transform)
|
||||
resize-x? (some? (:resize-vector modifiers))
|
||||
resize-y? (some? (:resize-vector-2 modifiers))
|
||||
|
||||
current-transform
|
||||
(if (some? (:resize-vector-2 modifiers))
|
||||
(gmt/multiply
|
||||
current-transform
|
||||
transform
|
||||
(gmt/scale-matrix (gpt/inverse (:resize-vector-2 modifiers)) (gpt/transform corner-pt transform-inverse))
|
||||
transform-inverse)
|
||||
current-transform)]
|
||||
current-transform))
|
||||
flip-x? (neg? (get-in modifiers [:resize-vector :x]))
|
||||
flip-y? (or (neg? (get-in modifiers [:resize-vector :y]))
|
||||
(neg? (get-in modifiers [:resize-vector-2 :y])))
|
||||
|
||||
result (cond-> (gmt/matrix)
|
||||
(and (some? transform) (or resize-x? resize-y?))
|
||||
(gmt/multiply transform)
|
||||
|
||||
resize-x?
|
||||
(gmt/scale (gpt/inverse (:resize-vector modifiers)) corner-pt)
|
||||
|
||||
resize-y?
|
||||
(gmt/scale (gpt/inverse (:resize-vector-2 modifiers)) corner-pt)
|
||||
|
||||
flip-x?
|
||||
(gmt/scale (gpt/point -1 1) corner-pt)
|
||||
|
||||
flip-y?
|
||||
(gmt/scale (gpt/point 1 -1) corner-pt)
|
||||
|
||||
(and (some? transform) (or resize-x? resize-y?))
|
||||
(gmt/multiply transform-inverse))
|
||||
|
||||
[width height]
|
||||
(if (or resize-x? resize-y?)
|
||||
(let [pc (-> (gpt/point x y)
|
||||
(gpt/transform transform)
|
||||
(gpt/transform current-transform))
|
||||
|
||||
pw (-> (gpt/point (+ x width) y)
|
||||
(gpt/transform transform)
|
||||
(gpt/transform current-transform))
|
||||
|
||||
ph (-> (gpt/point x (+ y height))
|
||||
(gpt/transform transform)
|
||||
(gpt/transform current-transform))]
|
||||
[(gpt/distance pc pw) (gpt/distance pc ph)])
|
||||
[width height])]
|
||||
|
||||
[result width height]))
|
||||
|
||||
(defn get-nodes
|
||||
"Retrieve the DOM nodes to apply the matrix transformation"
|
||||
|
@ -48,6 +72,7 @@
|
|||
|
||||
frame? (= :frame type)
|
||||
group? (= :group type)
|
||||
text? (= :text type)
|
||||
mask? (and group? masked-group?)
|
||||
|
||||
;; When the shape is a frame we maybe need to move its thumbnail
|
||||
|
@ -68,6 +93,11 @@
|
|||
group?
|
||||
[]
|
||||
|
||||
text?
|
||||
[shape-node
|
||||
(dom/query shape-node "foreignObject")
|
||||
(dom/query shape-node ".text-shape")]
|
||||
|
||||
:else
|
||||
[shape-node])))
|
||||
|
||||
|
@ -76,11 +106,23 @@
|
|||
(when-let [nodes (get-nodes shape)]
|
||||
(let [transform (get transforms id)
|
||||
modifiers (get-in modifiers [id :modifiers])
|
||||
transform (case type
|
||||
:text (text-corrected-transform shape transform modifiers)
|
||||
transform)]
|
||||
|
||||
[text-transform text-width text-height]
|
||||
(when (= :text type)
|
||||
(text-corrected-transform shape transform modifiers))]
|
||||
|
||||
(doseq [node nodes]
|
||||
(when (and (some? transform) (some? node))
|
||||
(cond
|
||||
(dom/class? node "text-shape")
|
||||
(when (some? text-transform)
|
||||
(dom/set-attribute node "transform" (str text-transform)))
|
||||
|
||||
(= (dom/get-tag-name node) "foreignObject")
|
||||
(when (and (some? text-width) (some? text-height))
|
||||
(dom/set-attribute node "width" text-width)
|
||||
(dom/set-attribute node "height" text-height))
|
||||
|
||||
(and (some? transform) (some? node))
|
||||
(dom/set-attribute node "transform" (str transform))))))))
|
||||
|
||||
(defn remove-transform [shapes]
|
||||
|
@ -88,7 +130,13 @@
|
|||
(when-let [nodes (get-nodes shape)]
|
||||
(doseq [node nodes]
|
||||
(when (some? node)
|
||||
(dom/remove-attribute node "transform"))))))
|
||||
(cond
|
||||
(= (dom/get-tag-name node) "foreignObject")
|
||||
;; The shape width/height will be automaticaly setup when the modifiers are applied
|
||||
nil
|
||||
|
||||
:else
|
||||
(dom/remove-attribute node "transform")))))))
|
||||
|
||||
(defn format-viewbox [vbox]
|
||||
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
||||
|
|
|
@ -17,21 +17,24 @@
|
|||
;; --- Deprecated methods
|
||||
|
||||
(defn event->inner-text
|
||||
[e]
|
||||
(.-innerText (.-target e)))
|
||||
[^js e]
|
||||
(when (some? e)
|
||||
(.-innerText (.-target e))))
|
||||
|
||||
(defn event->value
|
||||
[e]
|
||||
(.-value (.-target e)))
|
||||
[^js e]
|
||||
(when (some? e)
|
||||
(.-value (.-target e))))
|
||||
|
||||
(defn event->target
|
||||
[e]
|
||||
(.-target e))
|
||||
[^js e]
|
||||
(when (some? e)
|
||||
(.-target e)))
|
||||
|
||||
;; --- New methods
|
||||
|
||||
(defn set-html-title
|
||||
[title]
|
||||
[^string title]
|
||||
(set! (.-title globals/document) title))
|
||||
|
||||
(defn set-page-style
|
||||
|
@ -61,98 +64,117 @@
|
|||
(dom/getElement id))
|
||||
|
||||
(defn get-elements-by-tag
|
||||
[node tag]
|
||||
(.getElementsByTagName node tag))
|
||||
[^js node tag]
|
||||
(when (some? node)
|
||||
(.getElementsByTagName node tag)))
|
||||
|
||||
(defn stop-propagation
|
||||
[e]
|
||||
(when e
|
||||
(.stopPropagation e)))
|
||||
[^js event]
|
||||
(when event
|
||||
(.stopPropagation event)))
|
||||
|
||||
(defn prevent-default
|
||||
[e]
|
||||
(when e
|
||||
(.preventDefault e)))
|
||||
[^js event]
|
||||
(when event
|
||||
(.preventDefault event)))
|
||||
|
||||
(defn get-target
|
||||
"Extract the target from event instance."
|
||||
[event]
|
||||
(.-target event))
|
||||
[^js event]
|
||||
(when (some? event)
|
||||
(.-target event)))
|
||||
|
||||
(defn get-current-target
|
||||
"Extract the current target from event instance (different from target
|
||||
when event triggered in a child of the subscribing element)."
|
||||
[event]
|
||||
(.-currentTarget event))
|
||||
[^js event]
|
||||
(when (some? event)
|
||||
(.-currentTarget event)))
|
||||
|
||||
(defn get-parent
|
||||
[dom]
|
||||
(.-parentElement ^js dom))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.-parentElement ^js node)))
|
||||
|
||||
(defn get-value
|
||||
"Extract the value from dom node."
|
||||
[node]
|
||||
(.-value node))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.-value node)))
|
||||
|
||||
(defn get-attribute
|
||||
"Extract the value of one attribute of a dom node."
|
||||
[node attr-name]
|
||||
(.getAttribute ^js node attr-name))
|
||||
[^js node ^string attr-name]
|
||||
(when (some? node)
|
||||
(.getAttribute ^js node attr-name)))
|
||||
|
||||
(def get-target-val (comp get-value get-target))
|
||||
|
||||
(defn click
|
||||
"Click a node"
|
||||
[node]
|
||||
(.click node))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.click node)))
|
||||
|
||||
(defn get-files
|
||||
"Extract the files from dom node."
|
||||
[node]
|
||||
(array-seq (.-files node)))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(array-seq (.-files node))))
|
||||
|
||||
(defn checked?
|
||||
"Check if the node that represents a radio
|
||||
or checkbox is checked or not."
|
||||
[node]
|
||||
(.-checked node))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.-checked node)))
|
||||
|
||||
(defn valid?
|
||||
"Check if the node that is a form input
|
||||
has a valid value, against html5 form validation
|
||||
properties (required, min/max, pattern...)."
|
||||
[node]
|
||||
(.-valid (.-validity node)))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(when-let [validity (.-validity node)]
|
||||
(.-valid validity))))
|
||||
|
||||
(defn set-validity!
|
||||
"Manually set the validity status of a node that
|
||||
is a form input. If the state is an empty string,
|
||||
the input will be valid. If not, the string will
|
||||
be set as the error message."
|
||||
[node status]
|
||||
(.setCustomValidity node status)
|
||||
(.reportValidity node))
|
||||
[^js node status]
|
||||
(when (some? node)
|
||||
(.setCustomValidity node status)
|
||||
(.reportValidity node)))
|
||||
|
||||
(defn clean-value!
|
||||
[node]
|
||||
(set! (.-value node) ""))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(set! (.-value node) "")))
|
||||
|
||||
(defn set-value!
|
||||
[node value]
|
||||
(set! (.-value ^js node) value))
|
||||
[^js node value]
|
||||
(when (some? node)
|
||||
(set! (.-value ^js node) value)))
|
||||
|
||||
(defn select-text!
|
||||
[node]
|
||||
(.select ^js node))
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.select ^js node)))
|
||||
|
||||
(defn ^boolean equals?
|
||||
[node-a node-b]
|
||||
(.isEqualNode ^js node-a node-b))
|
||||
[^js node-a ^js node-b]
|
||||
|
||||
(or (and (nil? node-a) (nil? node-b))
|
||||
(and (some? node-a)
|
||||
(.isEqualNode ^js node-a node-b))))
|
||||
|
||||
(defn get-event-files
|
||||
"Extract the files from event instance."
|
||||
[event]
|
||||
(get-files (get-target event)))
|
||||
[^js event]
|
||||
(when (some? event)
|
||||
(get-files (get-target event))))
|
||||
|
||||
(defn create-element
|
||||
([tag]
|
||||
|
@ -161,50 +183,58 @@
|
|||
(.createElementNS globals/document ns tag)))
|
||||
|
||||
(defn set-html!
|
||||
[el html]
|
||||
(set! (.-innerHTML el) html))
|
||||
[^js el html]
|
||||
(when (some? el)
|
||||
(set! (.-innerHTML el) html)))
|
||||
|
||||
(defn append-child!
|
||||
[el child]
|
||||
(.appendChild ^js el child))
|
||||
[^js el child]
|
||||
(when (some? el)
|
||||
(.appendChild ^js el child)))
|
||||
|
||||
(defn get-first-child
|
||||
[el]
|
||||
(.-firstChild el))
|
||||
[^js el]
|
||||
(when (some? el)
|
||||
(.-firstChild el)))
|
||||
|
||||
(defn get-tag-name
|
||||
[el]
|
||||
(.-tagName el))
|
||||
[^js el]
|
||||
(when (some? el)
|
||||
(.-tagName el)))
|
||||
|
||||
(defn get-outer-html
|
||||
[el]
|
||||
(.-outerHTML el))
|
||||
[^js el]
|
||||
(when (some? el)
|
||||
(.-outerHTML el)))
|
||||
|
||||
(defn get-inner-text
|
||||
[el]
|
||||
(.-innerText el))
|
||||
[^js el]
|
||||
(when (some? el)
|
||||
(.-innerText el)))
|
||||
|
||||
(defn query
|
||||
[el query]
|
||||
[^js el ^string query]
|
||||
(when (some? el)
|
||||
(.querySelector el query)))
|
||||
|
||||
(defn get-client-position
|
||||
[event]
|
||||
[^js event]
|
||||
(let [x (.-clientX event)
|
||||
y (.-clientY event)]
|
||||
(gpt/point x y)))
|
||||
|
||||
(defn get-offset-position
|
||||
[event]
|
||||
(let [x (.-offsetX event)
|
||||
y (.-offsetY event)]
|
||||
(gpt/point x y)))
|
||||
[^js event]
|
||||
(when (some? event)
|
||||
(let [x (.-offsetX event)
|
||||
y (.-offsetY event)]
|
||||
(gpt/point x y))))
|
||||
|
||||
(defn get-client-size
|
||||
[node]
|
||||
{:width (.-clientWidth ^js node)
|
||||
:height (.-clientHeight ^js node)})
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
{:width (.-clientWidth ^js node)
|
||||
:height (.-clientHeight ^js node)}))
|
||||
|
||||
(defn get-bounding-rect
|
||||
[node]
|
||||
|
@ -222,12 +252,12 @@
|
|||
:height (.-innerHeight ^js js/window)})
|
||||
|
||||
(defn focus!
|
||||
[node]
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.focus node)))
|
||||
|
||||
(defn blur!
|
||||
[node]
|
||||
[^js node]
|
||||
(when (some? node)
|
||||
(.blur node)))
|
||||
|
||||
|
@ -245,8 +275,9 @@
|
|||
:hint "seems like the current browser does not support fullscreen api.")))
|
||||
|
||||
(defn ^boolean blob?
|
||||
[v]
|
||||
(instance? js/Blob v))
|
||||
[^js v]
|
||||
(when (some? v)
|
||||
(instance? js/Blob v)))
|
||||
|
||||
(defn create-blob
|
||||
"Create a blob from content."
|
||||
|
@ -265,20 +296,24 @@
|
|||
{:pre [(blob? b)]}
|
||||
(js/URL.createObjectURL b))
|
||||
|
||||
(defn set-property! [node property value]
|
||||
(.setAttribute node property value))
|
||||
(defn set-property! [^js node property value]
|
||||
(when (some? node)
|
||||
(.setAttribute node property value)))
|
||||
|
||||
(defn set-text! [node text]
|
||||
(set! (.-textContent node) text))
|
||||
(defn set-text! [^js node text]
|
||||
(when (some? node)
|
||||
(set! (.-textContent node) text)))
|
||||
|
||||
(defn set-css-property! [node property value]
|
||||
(.setProperty (.-style ^js node) property value))
|
||||
(defn set-css-property! [^js node property value]
|
||||
(when (some? node)
|
||||
(.setProperty (.-style ^js node) property value)))
|
||||
|
||||
(defn capture-pointer [event]
|
||||
(-> event get-target (.setPointerCapture (.-pointerId event))))
|
||||
(defn capture-pointer [^js event]
|
||||
(when (some? event)
|
||||
(-> event get-target (.setPointerCapture (.-pointerId event)))))
|
||||
|
||||
(defn release-pointer [event]
|
||||
(when (.-pointerId event)
|
||||
(defn release-pointer [^js event]
|
||||
(when (and (some? event) (.-pointerId event))
|
||||
(-> event get-target (.releasePointerCapture (.-pointerId event)))))
|
||||
|
||||
(defn get-root []
|
||||
|
@ -295,19 +330,23 @@
|
|||
(partition 2 params))))
|
||||
|
||||
(defn ^boolean class? [node class-name]
|
||||
(let [class-list (.-classList ^js node)]
|
||||
(.contains ^js class-list class-name)))
|
||||
(when (some? node)
|
||||
(let [class-list (.-classList ^js node)]
|
||||
(.contains ^js class-list class-name))))
|
||||
|
||||
(defn add-class! [node class-name]
|
||||
(let [class-list (.-classList ^js node)]
|
||||
(.add ^js class-list class-name)))
|
||||
(defn add-class! [^js node class-name]
|
||||
(when (some? node)
|
||||
(let [class-list (.-classList ^js node)]
|
||||
(.add ^js class-list class-name))))
|
||||
|
||||
(defn remove-class! [node class-name]
|
||||
(let [class-list (.-classList ^js node)]
|
||||
(.remove ^js class-list class-name)))
|
||||
(defn remove-class! [^js node class-name]
|
||||
(when (some? node)
|
||||
(let [class-list (.-classList ^js node)]
|
||||
(.remove ^js class-list class-name))))
|
||||
|
||||
(defn child? [node1 node2]
|
||||
(.contains ^js node2 ^js node1))
|
||||
(defn child? [^js node1 ^js node2]
|
||||
(when (some? node1)
|
||||
(.contains ^js node2 ^js node1)))
|
||||
|
||||
(defn get-user-agent []
|
||||
(.-userAgent globals/navigator))
|
||||
|
@ -315,11 +354,13 @@
|
|||
(defn get-active []
|
||||
(.-activeElement globals/document))
|
||||
|
||||
(defn active? [node]
|
||||
(= (get-active) node))
|
||||
(defn active? [^js node]
|
||||
(when (some? node)
|
||||
(= (get-active) node)))
|
||||
|
||||
(defn get-data [^js node ^string attr]
|
||||
(.getAttribute node (str "data-" attr)))
|
||||
(when (some? node)
|
||||
(.getAttribute node (str "data-" attr))))
|
||||
|
||||
(defn mtype->extension [mtype]
|
||||
;; https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types
|
||||
|
@ -336,42 +377,53 @@
|
|||
nil))
|
||||
|
||||
(defn set-attribute [^js node ^string attr value]
|
||||
(.setAttribute node attr value))
|
||||
(when (some? node)
|
||||
(.setAttribute node attr value)))
|
||||
|
||||
(defn remove-attribute [^js node ^string attr]
|
||||
(.removeAttribute node attr))
|
||||
(when (some? node)
|
||||
(.removeAttribute node attr)))
|
||||
|
||||
(defn get-scroll-pos
|
||||
[element]
|
||||
(.-scrollTop ^js element))
|
||||
[^js element]
|
||||
(when (some? element)
|
||||
(.-scrollTop element)))
|
||||
|
||||
(defn set-scroll-pos!
|
||||
[element scroll]
|
||||
(obj/set! ^js element "scrollTop" scroll))
|
||||
[^js element scroll]
|
||||
(when (some? element)
|
||||
(obj/set! element "scrollTop" scroll)))
|
||||
|
||||
(defn scroll-into-view!
|
||||
([element]
|
||||
(.scrollIntoView ^js element false))
|
||||
([element scroll-top]
|
||||
(.scrollIntoView ^js element scroll-top)))
|
||||
([^js element]
|
||||
(when (some? element)
|
||||
(.scrollIntoView element false)))
|
||||
|
||||
([^js element scroll-top]
|
||||
(when (some? element)
|
||||
(.scrollIntoView element scroll-top))))
|
||||
|
||||
(defn scroll-into-view-if-needed!
|
||||
([element]
|
||||
(.scrollIntoViewIfNeeded ^js element false))
|
||||
([element scroll-top]
|
||||
(.scrollIntoViewIfNeeded ^js element scroll-top)))
|
||||
([^js element]
|
||||
(when (some? element)
|
||||
(.scrollIntoViewIfNeeded ^js element false)))
|
||||
|
||||
([^js element scroll-top]
|
||||
(when (some? element)
|
||||
(.scrollIntoViewIfNeeded ^js element scroll-top))))
|
||||
|
||||
(defn is-in-viewport?
|
||||
[element]
|
||||
(let [rect (.getBoundingClientRect element)
|
||||
height (or (.-innerHeight js/window)
|
||||
(.. js/document -documentElement -clientHeight))
|
||||
width (or (.-innerWidth js/window)
|
||||
(.. js/document -documentElement -clientWidth))]
|
||||
(and (>= (.-top rect) 0)
|
||||
(>= (.-left rect) 0)
|
||||
(<= (.-bottom rect) height)
|
||||
(<= (.-right rect) width))))
|
||||
[^js element]
|
||||
(when (some? element)
|
||||
(let [rect (.getBoundingClientRect element)
|
||||
height (or (.-innerHeight js/window)
|
||||
(.. js/document -documentElement -clientHeight))
|
||||
width (or (.-innerWidth js/window)
|
||||
(.. js/document -documentElement -clientWidth))]
|
||||
(and (>= (.-top rect) 0)
|
||||
(>= (.-left rect) 0)
|
||||
(<= (.-bottom rect) height)
|
||||
(<= (.-right rect) width)))))
|
||||
|
||||
(defn trigger-download-uri
|
||||
[filename mtype uri]
|
||||
|
|
38
frontend/src/app/util/strings.cljs
Normal file
38
frontend/src/app/util/strings.cljs
Normal file
|
@ -0,0 +1,38 @@
|
|||
;; 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) UXBOX Labs SL
|
||||
|
||||
(ns app.util.strings
|
||||
(:require
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
|
||||
(def ^:const trail-zeros-regex-1 #"\.0+$")
|
||||
(def ^:const trail-zeros-regex-2 #"(\.\d*[^0])0+$")
|
||||
|
||||
(defn format-precision
|
||||
"Creates a number with predetermined precision and then removes the trailing 0.
|
||||
Examples:
|
||||
12.0123, 0 => 12
|
||||
12.0123, 1 => 12
|
||||
12.0123, 2 => 12.01"
|
||||
[num precision]
|
||||
|
||||
(try
|
||||
(if (number? num)
|
||||
(let [num-str (.toFixed num precision)
|
||||
|
||||
;; Remove all trailing zeros after the comma 100.00000
|
||||
num-str (str/replace num-str trail-zeros-regex-1 "")
|
||||
|
||||
;; Remove trailing zeros after a decimal number: 0.001|00|
|
||||
num-str (if-let [m (re-find trail-zeros-regex-2 num-str)]
|
||||
(str/replace num-str (first m) (second m))
|
||||
num-str)]
|
||||
num-str)
|
||||
(str num))
|
||||
(catch :default _
|
||||
(str num))))
|
||||
|
|
@ -111,6 +111,16 @@
|
|||
[state]
|
||||
(impl/cursorToEnd state))
|
||||
|
||||
(defn setup-block-styles
|
||||
[state blocks attrs]
|
||||
(if (empty? blocks)
|
||||
state
|
||||
(->> blocks
|
||||
(reduce
|
||||
(fn [state block-key]
|
||||
(impl/updateBlockData state block-key (clj->js attrs)))
|
||||
state))))
|
||||
|
||||
(defn apply-block-styles-to-content
|
||||
[state blocks]
|
||||
(if (empty? blocks)
|
||||
|
@ -130,3 +140,37 @@
|
|||
(defn insert-text [state text attrs]
|
||||
(let [style (txt/attrs-to-styles attrs)]
|
||||
(impl/insertText state text (clj->js attrs) (clj->js style))))
|
||||
|
||||
(defn get-style-override [state]
|
||||
(.getInlineStyleOverride state))
|
||||
|
||||
(defn set-style-override [state inline-style]
|
||||
(impl/setInlineStyleOverride state inline-style))
|
||||
|
||||
(defn content-equals [state other]
|
||||
(.equals (.getCurrentContent state) (.getCurrentContent other)))
|
||||
|
||||
(defn selection-equals [state other]
|
||||
(impl/selectionEquals (.getSelection state) (.getSelection other)))
|
||||
|
||||
(defn get-content-changes
|
||||
[old-state state]
|
||||
(let [old-blocks (js->clj (.toJS (.getBlockMap (.getCurrentContent ^js old-state)))
|
||||
:keywordize-keys false)
|
||||
new-blocks (js->clj (.toJS (.getBlockMap (.getCurrentContent ^js state)))
|
||||
:keywordize-keys false)]
|
||||
(merge
|
||||
(into {}
|
||||
(comp (filter #(contains? new-blocks (first %)))
|
||||
(map (fn [[bkey bstate]]
|
||||
[bkey
|
||||
{:old (get bstate "text")
|
||||
:new (get-in new-blocks [bkey "text"])}])))
|
||||
old-blocks)
|
||||
(into {}
|
||||
(comp (filter #(not (contains? old-blocks (first %))))
|
||||
(map (fn [[bkey bstate]]
|
||||
[bkey
|
||||
{:old nil
|
||||
:new (get bstate "text")}])))
|
||||
new-blocks))))
|
||||
|
|
|
@ -121,15 +121,33 @@ export function updateCurrentBlockData(state, attrs) {
|
|||
return EditorState.push(state, content, "change-block-data");
|
||||
}
|
||||
|
||||
function addStylesToOverride(styles, other) {
|
||||
let result = styles;
|
||||
|
||||
for (let style of other) {
|
||||
const [p, k, v] = style.split("$$$");
|
||||
const prefix = [p, k, ""].join("$$$");
|
||||
|
||||
const curValue = result.find((it) => it.startsWith(prefix))
|
||||
if (curValue) {
|
||||
result = result.remove(curValue);
|
||||
}
|
||||
result = result.add(style);
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
export function applyInlineStyle(state, styles) {
|
||||
const userSelection = state.getSelection();
|
||||
let selection = userSelection;
|
||||
let result = state;
|
||||
|
||||
if (selection.isCollapsed()) {
|
||||
selection = getSelectAllSelection(state);
|
||||
const currentOverride = state.getCurrentInlineStyle() || new OrderedSet();
|
||||
const styleOverride = addStylesToOverride(currentOverride, styles)
|
||||
return EditorState.setInlineStyleOverride(state, styleOverride);
|
||||
}
|
||||
|
||||
let result = state;
|
||||
let content = null;
|
||||
|
||||
for (let style of styles) {
|
||||
|
@ -300,6 +318,7 @@ export function getBlockData(state, blockKey) {
|
|||
|
||||
export function updateBlockData(state, blockKey, data) {
|
||||
const userSelection = state.getSelection();
|
||||
const inlineStyleOverride = state.getInlineStyleOverride();
|
||||
const content = state.getCurrentContent();
|
||||
const block = content.getBlockForKey(blockKey);
|
||||
const newBlock = mergeBlockData(block, data);
|
||||
|
@ -312,8 +331,10 @@ export function updateBlockData(state, blockKey, data) {
|
|||
blockData
|
||||
);
|
||||
|
||||
const result = EditorState.push(state, newContent, 'change-block-data');
|
||||
return EditorState.acceptSelection(result, userSelection);
|
||||
let result = EditorState.push(state, newContent, 'change-block-data');
|
||||
result = EditorState.acceptSelection(result, userSelection);
|
||||
result = EditorState.setInlineStyleOverride(result, inlineStyleOverride);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function getSelection(state) {
|
||||
|
@ -376,3 +397,15 @@ export function insertText(state, text, attrs, inlineStyles) {
|
|||
const resultSelection = SelectionState.createEmpty(selection.getStartKey());
|
||||
return EditorState.push(state, newContent, 'insert-fragment');
|
||||
}
|
||||
|
||||
export function setInlineStyleOverride(state, inlineStyles) {
|
||||
return EditorState.setInlineStyleOverride(state, inlineStyles);
|
||||
}
|
||||
|
||||
export function selectionEquals(selection, other) {
|
||||
return selection.getAnchorKey() === other.getAnchorKey() &&
|
||||
selection.getAnchorOffset() === other.getAnchorOffset() &&
|
||||
selection.getFocusKey() === other.getFocusKey() &&
|
||||
selection.getFocusOffset() === other.getFocusOffset() &&
|
||||
selection.getIsBackward() === other.getIsBackward();
|
||||
}
|
||||
|
|
|
@ -176,7 +176,9 @@
|
|||
(rx/tap #(reset! revn (:revn %)))
|
||||
(rx/ignore))
|
||||
|
||||
(rp/mutation :persist-temp-file {:id file-id}))))
|
||||
(->> (rp/mutation :persist-temp-file {:id file-id})
|
||||
;; We use merge to keep some information not stored in back-end
|
||||
(rx/map #(merge file %))))))
|
||||
|
||||
(defn upload-media-files
|
||||
"Upload a image to the backend and returns its id"
|
||||
|
@ -457,8 +459,7 @@
|
|||
|
||||
(let [progress-str (rx/subject)
|
||||
context (assoc context :progress progress-str)]
|
||||
(rx/merge
|
||||
progress-str
|
||||
[progress-str
|
||||
(->> (rx/of file)
|
||||
(rx/flat-map (partial process-pages context))
|
||||
(rx/tap #(progress! context :process-colors))
|
||||
|
@ -470,7 +471,7 @@
|
|||
(rx/tap #(progress! context :process-components))
|
||||
(rx/flat-map (partial process-library-components context))
|
||||
(rx/flat-map (partial send-changes context))
|
||||
(rx/tap #(rx/end! progress-str))))))
|
||||
(rx/tap #(rx/end! progress-str)))]))
|
||||
|
||||
(defn create-files
|
||||
[context files]
|
||||
|
@ -482,7 +483,6 @@
|
|||
(rx/flat-map
|
||||
(fn [context]
|
||||
(->> (create-file context)
|
||||
(rx/tap #(.log js/console "create-file" (clj->js %)))
|
||||
(rx/map #(vector % (first (get data (:file-id context)))))))))
|
||||
|
||||
(->> (rx/from files)
|
||||
|
@ -509,20 +509,29 @@
|
|||
(let [context {:project-id project-id
|
||||
:resolve (resolve-factory)}]
|
||||
(->> (create-files context files)
|
||||
(rx/catch #(.error js/console "IMPORT ERROR" %))
|
||||
(rx/catch #(.error js/console "IMPORT ERROR" (clj->js %)))
|
||||
(rx/flat-map
|
||||
(fn [[file data]]
|
||||
(->> (rx/concat
|
||||
(->> (uz/load-from-url (:uri data))
|
||||
(rx/map #(-> context (assoc :zip %) (merge data)))
|
||||
(rx/flat-map #(process-file % file)))
|
||||
(rx/of
|
||||
{:status :import-success
|
||||
:file-id (:file-id data)}))
|
||||
(rx/flat-map
|
||||
(fn [context]
|
||||
;; process file retrieves a stream that will emit progress notifications
|
||||
;; and other that will emit the files once imported
|
||||
(let [[progress-stream file-stream] (process-file context file)]
|
||||
(rx/merge
|
||||
progress-stream
|
||||
(->> file-stream
|
||||
(rx/map
|
||||
(fn [file]
|
||||
{:status :import-finish
|
||||
:errors (:errors file)
|
||||
:file-id (:file-id data)})))))))))
|
||||
|
||||
(rx/catch
|
||||
(fn [err]
|
||||
(.error js/console "ERROR" (:file-id data) err)
|
||||
(.error js/console "ERROR" (str (:file-id data)) (clj->js err) (clj->js (.-data err)))
|
||||
(rx/of {:status :import-error
|
||||
:file-id (:file-id data)
|
||||
:error (.-message err)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue