mirror of
https://github.com/penpot/penpot.git
synced 2025-06-07 04:21:39 +02:00
♻️ Replace slate editor with draft-js.
This commit is contained in:
parent
439e5ee6a1
commit
3bef80932d
28 changed files with 1272 additions and 981 deletions
|
@ -7,7 +7,8 @@
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.common.attrs)
|
(ns app.common.attrs
|
||||||
|
(:refer-clojure :exclude [merge]))
|
||||||
|
|
||||||
;; Extract some attributes of a list of shapes.
|
;; Extract some attributes of a list of shapes.
|
||||||
;; For each attribute, if the value is the same in all shapes,
|
;; For each attribute, if the value is the same in all shapes,
|
||||||
|
@ -48,7 +49,6 @@
|
||||||
(loop [attr (first attrs)
|
(loop [attr (first attrs)
|
||||||
attrs (rest attrs)
|
attrs (rest attrs)
|
||||||
result (transient {})]
|
result (transient {})]
|
||||||
|
|
||||||
(if attr
|
(if attr
|
||||||
(let [value
|
(let [value
|
||||||
(loop [curr (first objs)
|
(loop [curr (first objs)
|
||||||
|
@ -75,3 +75,12 @@
|
||||||
|
|
||||||
(persistent! result)))))
|
(persistent! result)))))
|
||||||
|
|
||||||
|
(defn merge
|
||||||
|
"Attrs specific merge function."
|
||||||
|
[obj attrs]
|
||||||
|
(reduce-kv (fn [obj k v]
|
||||||
|
(if (nil? v)
|
||||||
|
(dissoc obj k)
|
||||||
|
(assoc obj k v)))
|
||||||
|
obj
|
||||||
|
attrs))
|
||||||
|
|
79
common/app/common/text.cljc
Normal file
79
common/app/common/text.cljc
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.common.text
|
||||||
|
(:require
|
||||||
|
[app.common.attrs :as attrs]
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.util.transit :as t]
|
||||||
|
[clojure.walk :as walk]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
(def default-text-attrs
|
||||||
|
{:typography-ref-file nil
|
||||||
|
:typography-ref-id nil
|
||||||
|
:font-id "sourcesanspro"
|
||||||
|
:font-family "sourcesanspro"
|
||||||
|
:font-variant-id "regular"
|
||||||
|
:font-size "14"
|
||||||
|
:font-weight "400"
|
||||||
|
:font-style "normal"
|
||||||
|
:line-height "1.2"
|
||||||
|
:letter-spacing "0"
|
||||||
|
:text-transform "none"
|
||||||
|
:text-align "left"
|
||||||
|
:text-decoration "none"
|
||||||
|
:fill-color nil
|
||||||
|
:fill-opacity 1})
|
||||||
|
|
||||||
|
(def typography-fields
|
||||||
|
[:font-id
|
||||||
|
:font-family
|
||||||
|
:font-variant-id
|
||||||
|
:font-size
|
||||||
|
:font-weight
|
||||||
|
:font-style
|
||||||
|
:line-height
|
||||||
|
:letter-spacing
|
||||||
|
:text-transform])
|
||||||
|
|
||||||
|
(def default-typography
|
||||||
|
(merge
|
||||||
|
{:name "Source Sans Pro Regular"}
|
||||||
|
(select-keys default-text-attrs typography-fields)))
|
||||||
|
|
||||||
|
(defn transform-nodes
|
||||||
|
([transform root]
|
||||||
|
(transform-nodes identity transform root))
|
||||||
|
([pred transform root]
|
||||||
|
(walk/postwalk
|
||||||
|
(fn [item]
|
||||||
|
(if (and (map? item) (pred item))
|
||||||
|
(transform item)
|
||||||
|
item))
|
||||||
|
root)))
|
||||||
|
|
||||||
|
(defn node-seq
|
||||||
|
([root] (node-seq identity root))
|
||||||
|
([match? root]
|
||||||
|
(->> (tree-seq map? :children root)
|
||||||
|
(filter match?)
|
||||||
|
(seq))))
|
||||||
|
|
||||||
|
(defn ^boolean is-text-node?
|
||||||
|
[node]
|
||||||
|
(string? (:text node)))
|
||||||
|
|
||||||
|
(defn ^boolean is-paragraph-node?
|
||||||
|
[node]
|
||||||
|
(= "paragraph" (:type node)))
|
||||||
|
|
||||||
|
(defn ^boolean is-root-node?
|
||||||
|
[node]
|
||||||
|
(= "root" (:type node)))
|
|
@ -34,10 +34,10 @@
|
||||||
"shadow-cljs": "^2.11.20"
|
"shadow-cljs": "^2.11.20"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"humanize-duration": "~3.25.0",
|
|
||||||
"luxon": "~1.25.0",
|
|
||||||
"date-fns": "^2.19.0",
|
"date-fns": "^2.19.0",
|
||||||
|
"draft-js": "^0.11.7",
|
||||||
"highlight.js": "^10.6.0",
|
"highlight.js": "^10.6.0",
|
||||||
|
"humanize-duration": "~3.25.0",
|
||||||
"js-beautify": "^1.13.5",
|
"js-beautify": "^1.13.5",
|
||||||
"mousetrap": "^1.6.5",
|
"mousetrap": "^1.6.5",
|
||||||
"randomcolor": "^0.6.2",
|
"randomcolor": "^0.6.2",
|
||||||
|
|
|
@ -1,5 +1,69 @@
|
||||||
foreignObject .rich-text {
|
foreignObject {
|
||||||
color: $color-black;
|
.text-editor, .rich-text {
|
||||||
height: 100%;
|
color: $color-black;
|
||||||
white-space: pre-wrap;
|
height: 100%;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
font-family: sourcesanspro;
|
||||||
|
|
||||||
|
div {
|
||||||
|
line-height: inherit;
|
||||||
|
user-select: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
line-height: inherit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-editor {
|
||||||
|
.public-DraftStyleDefault-rtl {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
.public-DraftStyleDefault-rtl {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DraftEditor-root {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-top {
|
||||||
|
.DraftEditor-root {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-center {
|
||||||
|
.DraftEditor-root {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-bottom {
|
||||||
|
.DraftEditor-root {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text .paragraphs {
|
||||||
|
height: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&.align-top {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-center {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.align-bottom {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
[app.main.data.workspace.notifications :as dwn]
|
[app.main.data.workspace.notifications :as dwn]
|
||||||
[app.main.data.workspace.persistence :as dwp]
|
[app.main.data.workspace.persistence :as dwp]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.texts :as dwtxt]
|
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.transforms :as dwt]
|
||||||
[app.main.repo :as rp]
|
[app.main.repo :as rp]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
|
@ -603,22 +602,6 @@
|
||||||
(let [selected (get-in state [:workspace-local :selected])]
|
(let [selected (get-in state [:workspace-local :selected])]
|
||||||
(rx/from (map #(update-shape % attrs) selected))))))
|
(rx/from (map #(update-shape % attrs) selected))))))
|
||||||
|
|
||||||
(defn update-color-on-selected-shapes
|
|
||||||
[{:keys [fill-color stroke-color] :as attrs}]
|
|
||||||
(us/verify ::shape-attrs attrs)
|
|
||||||
(ptk/reify ::update-color-on-selected-shapes
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [selected (get-in state [:workspace-local :selected])
|
|
||||||
update-fn
|
|
||||||
(fn [shape]
|
|
||||||
(cond-> (merge shape attrs)
|
|
||||||
(and (= :text (:type shape))
|
|
||||||
(string? (:fill-color attrs)))
|
|
||||||
(dwtxt/impl-update-shape-attrs {:fill (:fill-color attrs)})))]
|
|
||||||
(rx/of (dwc/update-shapes-recursive selected update-fn))))))
|
|
||||||
|
|
||||||
|
|
||||||
;; --- Shape Movement (using keyboard shorcuts)
|
;; --- Shape Movement (using keyboard shorcuts)
|
||||||
|
|
||||||
(declare initial-selection-align)
|
(declare initial-selection-align)
|
||||||
|
@ -649,119 +632,13 @@
|
||||||
|
|
||||||
;; --- Delete Selected
|
;; --- Delete Selected
|
||||||
|
|
||||||
(defn- delete-shapes
|
|
||||||
[ids]
|
|
||||||
(us/assert (s/coll-of ::us/uuid) ids)
|
|
||||||
(ptk/reify ::delete-shapes
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [page-id (:current-page-id state)
|
|
||||||
objects (dwc/lookup-page-objects state page-id)
|
|
||||||
|
|
||||||
get-empty-parents
|
|
||||||
(fn [parents]
|
|
||||||
(->> parents
|
|
||||||
(map (fn [id]
|
|
||||||
(let [obj (get objects id)]
|
|
||||||
(when (and (= :group (:type obj))
|
|
||||||
(= 1 (count (:shapes obj))))
|
|
||||||
obj))))
|
|
||||||
(take-while (complement nil?))
|
|
||||||
(map :id)))
|
|
||||||
|
|
||||||
groups-to-unmask
|
|
||||||
(reduce (fn [group-ids id]
|
|
||||||
;; When the shape to delete is the mask of a masked group,
|
|
||||||
;; the mask condition must be removed, and it must be
|
|
||||||
;; converted to a normal group.
|
|
||||||
(let [obj (get objects id)
|
|
||||||
parent (get objects (:parent-id obj))]
|
|
||||||
(if (and (:masked-group? parent)
|
|
||||||
(= id (first (:shapes parent))))
|
|
||||||
(conj group-ids (:id parent))
|
|
||||||
group-ids)))
|
|
||||||
#{}
|
|
||||||
ids)
|
|
||||||
|
|
||||||
rchanges
|
|
||||||
(d/concat
|
|
||||||
(reduce (fn [res id]
|
|
||||||
(let [children (cp/get-children id objects)
|
|
||||||
parents (cp/get-parents id objects)
|
|
||||||
del-change #(array-map
|
|
||||||
:type :del-obj
|
|
||||||
:page-id page-id
|
|
||||||
:id %)]
|
|
||||||
(d/concat res
|
|
||||||
(map del-change (reverse children))
|
|
||||||
[(del-change id)]
|
|
||||||
(map del-change (get-empty-parents parents))
|
|
||||||
[{:type :reg-objects
|
|
||||||
:page-id page-id
|
|
||||||
:shapes (vec parents)}])))
|
|
||||||
[]
|
|
||||||
ids)
|
|
||||||
(map #(array-map
|
|
||||||
:type :mod-obj
|
|
||||||
:page-id page-id
|
|
||||||
:id %
|
|
||||||
:operations [{:type :set
|
|
||||||
:attr :masked-group?
|
|
||||||
:val false}])
|
|
||||||
groups-to-unmask))
|
|
||||||
|
|
||||||
uchanges
|
|
||||||
(d/concat
|
|
||||||
(reduce (fn [res id]
|
|
||||||
(let [children (cp/get-children id objects)
|
|
||||||
parents (cp/get-parents id objects)
|
|
||||||
parent (get objects (first parents))
|
|
||||||
add-change (fn [id]
|
|
||||||
(let [item (get objects id)]
|
|
||||||
{:type :add-obj
|
|
||||||
:id (:id item)
|
|
||||||
:page-id page-id
|
|
||||||
:index (cp/position-on-parent id objects)
|
|
||||||
:frame-id (:frame-id item)
|
|
||||||
:parent-id (:parent-id item)
|
|
||||||
:obj item}))]
|
|
||||||
(d/concat res
|
|
||||||
(map add-change (reverse (get-empty-parents parents)))
|
|
||||||
[(add-change id)]
|
|
||||||
(map add-change children)
|
|
||||||
[{:type :reg-objects
|
|
||||||
:page-id page-id
|
|
||||||
:shapes (vec parents)}]
|
|
||||||
(when (some? parent)
|
|
||||||
[{:type :mod-obj
|
|
||||||
:page-id page-id
|
|
||||||
:id (:id parent)
|
|
||||||
:operations [{:type :set-touched
|
|
||||||
:touched (:touched parent)}]}]))))
|
|
||||||
[]
|
|
||||||
ids)
|
|
||||||
(map #(array-map
|
|
||||||
:type :mod-obj
|
|
||||||
:page-id page-id
|
|
||||||
:id %
|
|
||||||
:operations [{:type :set
|
|
||||||
:attr :masked-group?
|
|
||||||
:val true}])
|
|
||||||
groups-to-unmask))]
|
|
||||||
|
|
||||||
;; (println "================ rchanges")
|
|
||||||
;; (cljs.pprint/pprint rchanges)
|
|
||||||
;; (println "================ uchanges")
|
|
||||||
;; (cljs.pprint/pprint uchanges)
|
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
|
|
||||||
|
|
||||||
(def delete-selected
|
(def delete-selected
|
||||||
"Deselect all and remove all selected shapes."
|
"Deselect all and remove all selected shapes."
|
||||||
(ptk/reify ::delete-selected
|
(ptk/reify ::delete-selected
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [selected (get-in state [:workspace-local :selected])]
|
(let [selected (get-in state [:workspace-local :selected])]
|
||||||
(rx/of (delete-shapes selected)
|
(rx/of (dwc/delete-shapes selected)
|
||||||
(dws/deselect-all))))))
|
(dws/deselect-all))))))
|
||||||
|
|
||||||
;; --- Shape Vertical Ordering
|
;; --- Shape Vertical Ordering
|
||||||
|
|
|
@ -395,7 +395,6 @@
|
||||||
;; Shapes
|
;; Shapes
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
|
||||||
(defn expand-all-parents
|
(defn expand-all-parents
|
||||||
[ids objects]
|
[ids objects]
|
||||||
(ptk/reify ::expand-all-parents
|
(ptk/reify ::expand-all-parents
|
||||||
|
@ -672,6 +671,114 @@
|
||||||
:shapes [shape-id]})))]
|
:shapes [shape-id]})))]
|
||||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
|
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||||
|
|
||||||
|
|
||||||
|
(defn delete-shapes
|
||||||
|
[ids]
|
||||||
|
(us/assert (s/coll-of ::us/uuid) ids)
|
||||||
|
(ptk/reify ::delete-shapes
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [page-id (:current-page-id state)
|
||||||
|
objects (lookup-page-objects state page-id)
|
||||||
|
|
||||||
|
get-empty-parents
|
||||||
|
(fn [parents]
|
||||||
|
(->> parents
|
||||||
|
(map (fn [id]
|
||||||
|
(let [obj (get objects id)]
|
||||||
|
(when (and (= :group (:type obj))
|
||||||
|
(= 1 (count (:shapes obj))))
|
||||||
|
obj))))
|
||||||
|
(take-while (complement nil?))
|
||||||
|
(map :id)))
|
||||||
|
|
||||||
|
groups-to-unmask
|
||||||
|
(reduce (fn [group-ids id]
|
||||||
|
;; When the shape to delete is the mask of a masked group,
|
||||||
|
;; the mask condition must be removed, and it must be
|
||||||
|
;; converted to a normal group.
|
||||||
|
(let [obj (get objects id)
|
||||||
|
parent (get objects (:parent-id obj))]
|
||||||
|
(if (and (:masked-group? parent)
|
||||||
|
(= id (first (:shapes parent))))
|
||||||
|
(conj group-ids (:id parent))
|
||||||
|
group-ids)))
|
||||||
|
#{}
|
||||||
|
ids)
|
||||||
|
|
||||||
|
rchanges
|
||||||
|
(d/concat
|
||||||
|
(reduce (fn [res id]
|
||||||
|
(let [children (cp/get-children id objects)
|
||||||
|
parents (cp/get-parents id objects)
|
||||||
|
del-change #(array-map
|
||||||
|
:type :del-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id %)]
|
||||||
|
(d/concat res
|
||||||
|
(map del-change (reverse children))
|
||||||
|
[(del-change id)]
|
||||||
|
(map del-change (get-empty-parents parents))
|
||||||
|
[{:type :reg-objects
|
||||||
|
:page-id page-id
|
||||||
|
:shapes (vec parents)}])))
|
||||||
|
[]
|
||||||
|
ids)
|
||||||
|
(map #(array-map
|
||||||
|
:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id %
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :masked-group?
|
||||||
|
:val false}])
|
||||||
|
groups-to-unmask))
|
||||||
|
|
||||||
|
uchanges
|
||||||
|
(d/concat
|
||||||
|
(reduce (fn [res id]
|
||||||
|
(let [children (cp/get-children id objects)
|
||||||
|
parents (cp/get-parents id objects)
|
||||||
|
parent (get objects (first parents))
|
||||||
|
add-change (fn [id]
|
||||||
|
(let [item (get objects id)]
|
||||||
|
{:type :add-obj
|
||||||
|
:id (:id item)
|
||||||
|
:page-id page-id
|
||||||
|
:index (cp/position-on-parent id objects)
|
||||||
|
:frame-id (:frame-id item)
|
||||||
|
:parent-id (:parent-id item)
|
||||||
|
:obj item}))]
|
||||||
|
(d/concat res
|
||||||
|
(map add-change (reverse (get-empty-parents parents)))
|
||||||
|
[(add-change id)]
|
||||||
|
(map add-change children)
|
||||||
|
[{:type :reg-objects
|
||||||
|
:page-id page-id
|
||||||
|
:shapes (vec parents)}]
|
||||||
|
(when (some? parent)
|
||||||
|
[{:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id (:id parent)
|
||||||
|
:operations [{:type :set-touched
|
||||||
|
:touched (:touched parent)}]}]))))
|
||||||
|
[]
|
||||||
|
ids)
|
||||||
|
(map #(array-map
|
||||||
|
:type :mod-obj
|
||||||
|
:page-id page-id
|
||||||
|
:id %
|
||||||
|
:operations [{:type :set
|
||||||
|
:attr :masked-group?
|
||||||
|
:val true}])
|
||||||
|
groups-to-unmask))]
|
||||||
|
|
||||||
|
;; (println "================ rchanges")
|
||||||
|
;; (cljs.pprint/pprint rchanges)
|
||||||
|
;; (println "================ uchanges")
|
||||||
|
;; (cljs.pprint/pprint uchanges)
|
||||||
|
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||||
|
|
||||||
|
|
||||||
;; --- Add shape to Workspace
|
;; --- Add shape to Workspace
|
||||||
|
|
||||||
(defn- viewport-center
|
(defn- viewport-center
|
||||||
|
|
|
@ -5,20 +5,20 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.data.workspace.libraries-helpers
|
(ns app.main.data.workspace.libraries-helpers
|
||||||
(:require
|
(:require
|
||||||
[cljs.spec.alpha :as s]
|
|
||||||
[clojure.set :as set]
|
|
||||||
[app.common.spec :as us]
|
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
|
[app.common.spec :as us]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.main.data.workspace.groups :as dwg]
|
[app.main.data.workspace.groups :as dwg]
|
||||||
[app.util.logging :as log]
|
[app.util.logging :as log]
|
||||||
[app.util.text :as ut]))
|
[cljs.spec.alpha :as s]
|
||||||
|
[clojure.set :as set]))
|
||||||
|
|
||||||
;; Change this to :info :debug or :trace to debug this module
|
;; Change this to :info :debug or :trace to debug this module
|
||||||
(log/set-level! :warn)
|
(log/set-level! :warn)
|
||||||
|
@ -317,11 +317,11 @@
|
||||||
(->> shape
|
(->> shape
|
||||||
:content
|
:content
|
||||||
;; Check if any node in the content has a reference for the library
|
;; Check if any node in the content has a reference for the library
|
||||||
(ut/some-node
|
(txt/node-seq
|
||||||
#(or (and (some? (:stroke-color-ref-id %))
|
#(or (and (some? (:stroke-color-ref-id %))
|
||||||
(= library-id (:stroke-color-ref-file %)))
|
(= library-id (:stroke-color-ref-file %)))
|
||||||
(and (some? (:fill-color-ref-id %))
|
(and (some? (:fill-color-ref-id %))
|
||||||
(= library-id (:fill-color-ref-file %))))))
|
(= library-id (:fill-color-ref-file %))))))
|
||||||
(some
|
(some
|
||||||
#(let [attr (name %)
|
#(let [attr (name %)
|
||||||
attr-ref-id (keyword (str attr "-ref-id"))
|
attr-ref-id (keyword (str attr "-ref-id"))
|
||||||
|
@ -336,9 +336,9 @@
|
||||||
(->> shape
|
(->> shape
|
||||||
:content
|
:content
|
||||||
;; Check if any node in the content has a reference for the library
|
;; Check if any node in the content has a reference for the library
|
||||||
(ut/some-node
|
(txt/node-seq
|
||||||
#(and (some? (:typography-ref-id %))
|
#(and (some? (:typography-ref-id %))
|
||||||
(= library-id (:typography-ref-file %)))))))))
|
(= library-id (:typography-ref-file %)))))))))
|
||||||
|
|
||||||
(defmulti generate-sync-shape
|
(defmulti generate-sync-shape
|
||||||
"Generate changes to synchronize one shape with all assets of the given type
|
"Generate changes to synchronize one shape with all assets of the given type
|
||||||
|
@ -356,7 +356,7 @@
|
||||||
(defn- generate-sync-text-shape
|
(defn- generate-sync-text-shape
|
||||||
[shape container update-node]
|
[shape container update-node]
|
||||||
(let [old-content (:content shape)
|
(let [old-content (:content shape)
|
||||||
new-content (ut/map-node update-node old-content)
|
new-content (txt/transform-nodes update-node old-content)
|
||||||
rchanges [(make-change
|
rchanges [(make-change
|
||||||
container
|
container
|
||||||
{:type :mod-obj
|
{:type :mod-obj
|
||||||
|
|
|
@ -5,199 +5,188 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.data.workspace.texts
|
(ns app.main.data.workspace.texts
|
||||||
(:require
|
(:require
|
||||||
["slate" :as slate :refer [Editor Node Transforms Text]]
|
|
||||||
["slate-react" :as rslate]
|
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.attrs :as attrs]
|
[app.common.attrs :as attrs]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.main.data.workspace.selection :as dws]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.transforms :as dwt]
|
[app.main.data.workspace.transforms :as dwt]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.util.text :as ut]
|
[app.util.text-editor :as ted]
|
||||||
|
[app.util.timers :as ts]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[cljs.spec.alpha :as s]
|
[cljs.spec.alpha :as s]
|
||||||
[clojure.walk :as walk]
|
|
||||||
[goog.object :as gobj]
|
[goog.object :as gobj]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[potok.core :as ptk]))
|
[potok.core :as ptk]))
|
||||||
|
|
||||||
(defn create-editor
|
(defn update-editor
|
||||||
[]
|
[editor]
|
||||||
(rslate/withReact (slate/createEditor)))
|
(ptk/reify ::update-editor
|
||||||
|
|
||||||
(defn assign-editor
|
|
||||||
[id editor]
|
|
||||||
(ptk/reify ::assign-editor
|
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(-> state
|
(if (some? editor)
|
||||||
(assoc-in [:workspace-local :editors id] editor)
|
(assoc state :workspace-editor editor)
|
||||||
(update-in [:workspace-local :editor-n] (fnil inc 0))))))
|
(dissoc state :workspace-editor)))))
|
||||||
|
|
||||||
|
(defn focus-editor
|
||||||
|
[]
|
||||||
|
(ptk/reify ::focus-editor
|
||||||
|
ptk/EffectEvent
|
||||||
|
(effect [_ state stream]
|
||||||
|
(when-let [editor (:workspace-editor state)]
|
||||||
|
(ts/schedule #(.focus ^js editor))))))
|
||||||
|
|
||||||
|
(defn update-editor-state
|
||||||
|
[{:keys [id] :as shape} editor-state]
|
||||||
|
(ptk/reify ::update-editor-state
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(if (some? editor-state)
|
||||||
|
(update state :workspace-editor-state assoc id editor-state)
|
||||||
|
(update state :workspace-editor-state dissoc id)))))
|
||||||
|
|
||||||
|
(defn initialize-editor-state
|
||||||
|
[{:keys [id content] :as shape}]
|
||||||
|
(ptk/reify ::initialize-editor-state
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(update-in state [:workspace-editor-state id]
|
||||||
|
(fn [_]
|
||||||
|
(ted/create-editor-state
|
||||||
|
(some->> content ted/import-content)))))))
|
||||||
|
|
||||||
|
(defn finalize-editor-state
|
||||||
|
[{:keys [id] :as shape}]
|
||||||
|
(ptk/reify ::finalize-editor-state
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [content (-> (get-in state [:workspace-editor-state id])
|
||||||
|
(ted/get-editor-current-content))]
|
||||||
|
(if (ted/content-has-text? content)
|
||||||
|
(let [content (d/merge (ted/export-content content)
|
||||||
|
(dissoc (:content shape) :children))]
|
||||||
|
(rx/merge
|
||||||
|
(rx/of (update-editor-state shape nil))
|
||||||
|
(when (not= content (:content shape))
|
||||||
|
(rx/of (dwc/update-shapes [id] #(assoc % :content content))))))
|
||||||
|
(rx/of (dws/deselect-shape id)
|
||||||
|
(dwc/delete-shapes [id])))))))
|
||||||
|
|
||||||
|
(defn select-all
|
||||||
|
"Select all content of the current editor. When not editor found this
|
||||||
|
event is noop."
|
||||||
|
[{:keys [id] :as shape}]
|
||||||
|
(ptk/reify ::editor-select-all
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(d/update-in-when state [:workspace-editor-state id] ted/editor-select-all))))
|
||||||
|
|
||||||
;; --- Helpers
|
;; --- Helpers
|
||||||
|
|
||||||
(defn- calculate-full-selection
|
|
||||||
[editor]
|
|
||||||
(let [children (obj/get editor "children")
|
|
||||||
paragraphs (obj/get-in children [0 "children" 0 "children"])
|
|
||||||
lastp (aget paragraphs (dec (alength paragraphs)))
|
|
||||||
lastptxt (.string Node lastp)]
|
|
||||||
#js {:anchor #js {:path #js [0 0 0]
|
|
||||||
:offset 0}
|
|
||||||
:focus #js {:path #js [0 0 (dec (alength paragraphs))]
|
|
||||||
:offset (alength lastptxt)}}))
|
|
||||||
|
|
||||||
(defn- editor-select-all!
|
|
||||||
[editor]
|
|
||||||
(let [children (obj/get editor "children")
|
|
||||||
paragraphs (obj/get-in children [0 "children" 0 "children"])
|
|
||||||
range (calculate-full-selection editor)]
|
|
||||||
(.select Transforms editor range)))
|
|
||||||
|
|
||||||
(defn- editor-set!
|
|
||||||
([editor props]
|
|
||||||
(editor-set! editor props #js {}))
|
|
||||||
([editor props options]
|
|
||||||
(.setNodes Transforms editor props options)
|
|
||||||
editor))
|
|
||||||
|
|
||||||
(defn- transform-nodes
|
|
||||||
[pred transform data]
|
|
||||||
(walk/postwalk
|
|
||||||
(fn [item]
|
|
||||||
(if (and (map? item) (pred item))
|
|
||||||
(transform item)
|
|
||||||
item))
|
|
||||||
data))
|
|
||||||
|
|
||||||
;; --- Editor Related Helpers
|
|
||||||
|
|
||||||
(defn- ^boolean is-text-node?
|
|
||||||
[node]
|
|
||||||
(cond
|
|
||||||
(object? node) (.isText Text node)
|
|
||||||
(map? node) (string? (:text node))
|
|
||||||
(nil? node) false
|
|
||||||
:else (throw (ex-info "unexpected type" {:node node}))))
|
|
||||||
|
|
||||||
(defn- ^boolean is-paragraph-node?
|
|
||||||
[node]
|
|
||||||
(cond
|
|
||||||
(object? node) (= (.-type node) "paragraph")
|
|
||||||
(map? node) (= "paragraph" (:type node))
|
|
||||||
(nil? node) false
|
|
||||||
:else (throw (ex-info "unexpected type" {:node node}))))
|
|
||||||
|
|
||||||
(defn- ^boolean is-root-node?
|
|
||||||
[node]
|
|
||||||
(cond
|
|
||||||
(object? node) (= (.-type node) "root")
|
|
||||||
(map? node) (= "root" (:type node))
|
|
||||||
(nil? node) false
|
|
||||||
:else (throw (ex-info "unexpected type" {:node node}))))
|
|
||||||
|
|
||||||
(defn- editor-current-values
|
|
||||||
[editor pred attrs universal?]
|
|
||||||
(let [options #js {:match pred :universal universal?}
|
|
||||||
_ (when (nil? (obj/get editor "selection"))
|
|
||||||
(obj/set! options "at" (calculate-full-selection editor)))
|
|
||||||
result (.nodes Editor editor options)
|
|
||||||
match (ffirst (es6-iterator-seq result))]
|
|
||||||
(when (object? match)
|
|
||||||
(let [attrs (clj->js attrs)
|
|
||||||
result (areduce attrs i ret #js {}
|
|
||||||
(let [val (obj/get match (aget attrs i))]
|
|
||||||
(if val
|
|
||||||
(obj/set! ret (aget attrs i) val)
|
|
||||||
ret)))]
|
|
||||||
(js->clj result :keywordize-keys true)))))
|
|
||||||
|
|
||||||
(defn nodes-seq
|
|
||||||
[match? node]
|
|
||||||
(->> (tree-seq map? :children node)
|
|
||||||
(filter match?)))
|
|
||||||
|
|
||||||
(defn- shape-current-values
|
(defn- shape-current-values
|
||||||
[shape pred attrs]
|
[shape pred attrs]
|
||||||
(let [root (:content shape)
|
(let [root (:content shape)
|
||||||
nodes (->> (nodes-seq pred root)
|
nodes (->> (txt/node-seq pred root)
|
||||||
(map #(if (is-text-node? %)
|
(map #(if (txt/is-text-node? %)
|
||||||
(merge ut/default-text-attrs %)
|
(merge txt/default-text-attrs %)
|
||||||
%)))]
|
%)))]
|
||||||
(attrs/get-attrs-multi nodes attrs)))
|
(attrs/get-attrs-multi nodes attrs)))
|
||||||
|
|
||||||
(defn current-text-values
|
|
||||||
[{:keys [editor default attrs shape]}]
|
|
||||||
(if editor
|
|
||||||
(editor-current-values editor is-text-node? attrs true)
|
|
||||||
(shape-current-values shape is-text-node? attrs)))
|
|
||||||
|
|
||||||
(defn current-paragraph-values
|
(defn current-paragraph-values
|
||||||
[{:keys [editor attrs shape]}]
|
[{:keys [editor-state attrs shape]}]
|
||||||
(if editor
|
(if editor-state
|
||||||
(editor-current-values editor is-paragraph-node? attrs false)
|
(-> (ted/get-editor-current-block-data editor-state)
|
||||||
(shape-current-values shape is-paragraph-node? attrs)))
|
(select-keys attrs))
|
||||||
|
(shape-current-values shape txt/is-paragraph-node? attrs)))
|
||||||
|
|
||||||
(defn current-root-values
|
(defn current-text-values
|
||||||
[{:keys [editor attrs shape]}]
|
[{:keys [editor-state attrs shape]}]
|
||||||
(if editor
|
(if editor-state
|
||||||
(editor-current-values editor is-root-node? attrs false)
|
(-> (ted/get-editor-current-inline-styles editor-state)
|
||||||
(shape-current-values shape is-root-node? attrs)))
|
(select-keys attrs))
|
||||||
|
(shape-current-values shape txt/is-text-node? attrs)))
|
||||||
|
|
||||||
(defn- merge-attrs
|
|
||||||
[node attrs]
|
|
||||||
(reduce-kv (fn [node k v]
|
|
||||||
(if (nil? v)
|
|
||||||
(dissoc node k)
|
|
||||||
(assoc node k v)))
|
|
||||||
node
|
|
||||||
attrs))
|
|
||||||
|
|
||||||
(defn impl-update-shape-attrs
|
;; --- TEXT EDITION IMPL
|
||||||
([shape attrs]
|
|
||||||
;; NOTE: this arity is used in workspace for properly update the
|
|
||||||
;; fill color using colorpalette, then the predicate should be
|
|
||||||
;; defined.
|
|
||||||
(impl-update-shape-attrs shape attrs is-text-node?))
|
|
||||||
([{:keys [type content] :as shape} attrs pred]
|
|
||||||
(assert (= :text type) "should be shape type")
|
|
||||||
(let [merge-attrs #(merge-attrs % attrs)]
|
|
||||||
(update shape :content #(transform-nodes pred merge-attrs %)))))
|
|
||||||
|
|
||||||
(defn update-attrs
|
(defn- update-shape
|
||||||
[{:keys [id editor attrs pred split]
|
[shape pred-fn attrs]
|
||||||
:or {pred is-text-node?}}]
|
(let [merge-attrs #(attrs/merge % attrs)
|
||||||
(if editor
|
transform #(txt/transform-nodes pred-fn merge-attrs %)]
|
||||||
(ptk/reify ::update-attrs
|
(update shape :content transform)))
|
||||||
ptk/EffectEvent
|
|
||||||
(effect [_ state stream]
|
|
||||||
(editor-set! editor (clj->js attrs) #js {:match pred :split split})))
|
|
||||||
|
|
||||||
(ptk/reify ::update-attrs
|
|
||||||
ptk/WatchEvent
|
|
||||||
(watch [_ state stream]
|
|
||||||
(let [objects (dwc/lookup-page-objects state)
|
|
||||||
shape (get objects id)
|
|
||||||
ids (cond (= (:type shape) :text) [id]
|
|
||||||
(= (:type shape) :group) (cp/get-children id objects))]
|
|
||||||
(rx/of (dwc/update-shapes ids #(impl-update-shape-attrs % attrs pred))))))))
|
|
||||||
|
|
||||||
(defn update-text-attrs
|
|
||||||
[options]
|
|
||||||
(update-attrs (assoc options :pred is-text-node? :split true)))
|
|
||||||
|
|
||||||
(defn update-paragraph-attrs
|
|
||||||
[options]
|
|
||||||
(update-attrs (assoc options :pred is-paragraph-node? :split false)))
|
|
||||||
|
|
||||||
(defn update-root-attrs
|
(defn update-root-attrs
|
||||||
[options]
|
[{:keys [id attrs]}]
|
||||||
(update-attrs (assoc options :pred is-root-node? :split false)))
|
(ptk/reify ::update-root-attrs
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(let [objects (dwc/lookup-page-objects state)
|
||||||
|
shape (get objects id)
|
||||||
|
|
||||||
|
update-fn #(update-shape % txt/is-root-node? attrs)
|
||||||
|
shape-ids (cond (= (:type shape) :text) [id]
|
||||||
|
(= (:type shape) :group) (cp/get-children id objects))]
|
||||||
|
|
||||||
|
(rx/of (dwc/update-shapes shape-ids update-fn)
|
||||||
|
(focus-editor))))))
|
||||||
|
|
||||||
|
(defn update-paragraph-attrs
|
||||||
|
[{:keys [id attrs]}]
|
||||||
|
(let [attrs (d/without-nils attrs)]
|
||||||
|
(ptk/reify ::update-paragraph-attrs
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(d/update-in-when state [:workspace-editor-state id] ted/update-editor-current-block-data attrs))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(cond
|
||||||
|
(some? (get-in state [:workspace-editor-state id]))
|
||||||
|
(rx/of (focus-editor))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [objects (dwc/lookup-page-objects state)
|
||||||
|
shape (get objects id)
|
||||||
|
|
||||||
|
update-fn #(update-shape % txt/is-paragraph-node? attrs)
|
||||||
|
shape-ids (cond (= (:type shape) :text) [id]
|
||||||
|
(= (:type shape) :group) (cp/get-children id objects))]
|
||||||
|
|
||||||
|
(rx/of (dwc/update-shapes shape-ids update-fn))))))))
|
||||||
|
|
||||||
|
(defn update-text-attrs
|
||||||
|
[{:keys [id attrs]}]
|
||||||
|
(let [attrs (d/without-nils attrs)]
|
||||||
|
(ptk/reify ::update-text-attrs
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(d/update-in-when state [:workspace-editor-state id] ted/update-editor-current-inline-styles attrs))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(cond
|
||||||
|
(some? (get-in state [:workspace-editor-state id]))
|
||||||
|
(rx/of (focus-editor))
|
||||||
|
|
||||||
|
:else
|
||||||
|
(let [objects (dwc/lookup-page-objects state)
|
||||||
|
shape (get objects id)
|
||||||
|
|
||||||
|
update-fn #(update-shape % txt/is-text-node? attrs)
|
||||||
|
shape-ids (cond (= (:type shape) :text) [id]
|
||||||
|
(= (:type shape) :group) (cp/get-children id objects))]
|
||||||
|
(rx/of (dwc/update-shapes shape-ids update-fn))))))))
|
||||||
|
|
||||||
|
;; --- RESIZE UTILS
|
||||||
|
|
||||||
(defn update-overflow-text [id value]
|
(defn update-overflow-text [id value]
|
||||||
(ptk/reify ::update-overflow-text
|
(ptk/reify ::update-overflow-text
|
||||||
|
@ -211,7 +200,7 @@
|
||||||
(ptk/reify ::start-edit-if-selected
|
(ptk/reify ::start-edit-if-selected
|
||||||
ptk/UpdateEvent
|
ptk/UpdateEvent
|
||||||
(update [_ state]
|
(update [_ state]
|
||||||
(let [objects (dwc/lookup-page-objects state)
|
(let [objects (dwc/lookup-page-objects state)
|
||||||
selected (->> state :workspace-local :selected (map #(get objects %)))]
|
selected (->> state :workspace-local :selected (map #(get objects %)))]
|
||||||
(cond-> state
|
(cond-> state
|
||||||
(and (= 1 (count selected))
|
(and (= 1 (count selected))
|
||||||
|
@ -284,7 +273,8 @@
|
||||||
;; together. This improves the performance because we only re-render the
|
;; together. This improves the performance because we only re-render the
|
||||||
;; resized components once even if there are changes that applies to
|
;; resized components once even if there are changes that applies to
|
||||||
;; lots of texts like changing a font
|
;; lots of texts like changing a font
|
||||||
(defn resize-text [id new-width new-height]
|
(defn resize-text
|
||||||
|
[id new-width new-height]
|
||||||
(ptk/reify ::resize-text
|
(ptk/reify ::resize-text
|
||||||
IDeref
|
IDeref
|
||||||
(-deref [_]
|
(-deref [_]
|
||||||
|
|
|
@ -180,6 +180,12 @@
|
||||||
(def workspace-frames
|
(def workspace-frames
|
||||||
(l/derived cp/select-frames workspace-page-objects))
|
(l/derived cp/select-frames workspace-page-objects))
|
||||||
|
|
||||||
|
(def workspace-editor
|
||||||
|
(l/derived :workspace-editor st/state))
|
||||||
|
|
||||||
|
(def workspace-editor-state
|
||||||
|
(l/derived :workspace-editor-state st/state))
|
||||||
|
|
||||||
(defn object-by-id
|
(defn object-by-id
|
||||||
[id]
|
[id]
|
||||||
(l/derived #(get % id) workspace-page-objects))
|
(l/derived #(get % id) workspace-page-objects))
|
||||||
|
|
|
@ -9,35 +9,35 @@
|
||||||
|
|
||||||
(ns app.main.ui
|
(ns app.main.ui
|
||||||
(:require
|
(:require
|
||||||
[app.config :as cfg]
|
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.uuid :as uuid]
|
|
||||||
[app.common.spec :as us]
|
[app.common.spec :as us]
|
||||||
|
[app.common.uuid :as uuid]
|
||||||
|
[app.config :as cfg]
|
||||||
[app.main.data.auth :refer [logout]]
|
[app.main.data.auth :refer [logout]]
|
||||||
[app.main.data.messages :as dm]
|
[app.main.data.messages :as dm]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.ui.auth :refer [auth]]
|
[app.main.ui.auth :refer [auth]]
|
||||||
[app.main.ui.auth.verify-token :refer [verify-token]]
|
[app.main.ui.auth.verify-token :refer [verify-token]]
|
||||||
[app.main.ui.cursors :as c]
|
|
||||||
[app.main.ui.context :as ctx]
|
[app.main.ui.context :as ctx]
|
||||||
[app.main.ui.onboarding]
|
[app.main.ui.cursors :as c]
|
||||||
[app.main.ui.dashboard :refer [dashboard]]
|
[app.main.ui.dashboard :refer [dashboard]]
|
||||||
|
[app.main.ui.handoff :refer [handoff]]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.messages :as msgs]
|
[app.main.ui.messages :as msgs]
|
||||||
|
[app.main.ui.onboarding]
|
||||||
[app.main.ui.render :as render]
|
[app.main.ui.render :as render]
|
||||||
[app.main.ui.settings :as settings]
|
[app.main.ui.settings :as settings]
|
||||||
[app.main.ui.static :as static]
|
[app.main.ui.static :as static]
|
||||||
[app.main.ui.viewer :refer [viewer-page]]
|
[app.main.ui.viewer :refer [viewer-page]]
|
||||||
[app.main.ui.handoff :refer [handoff]]
|
|
||||||
[app.main.ui.workspace :as workspace]
|
[app.main.ui.workspace :as workspace]
|
||||||
[app.util.i18n :as i18n :refer [tr t]]
|
[app.util.i18n :as i18n :refer [tr t]]
|
||||||
[app.util.timers :as ts]
|
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
[cuerdas.core :as str]
|
[app.util.timers :as ts]
|
||||||
[cljs.spec.alpha :as s]
|
|
||||||
[cljs.pprint :refer [pprint]]
|
[cljs.pprint :refer [pprint]]
|
||||||
|
[cljs.spec.alpha :as s]
|
||||||
|
[cuerdas.core :as str]
|
||||||
[expound.alpha :as expound]
|
[expound.alpha :as expound]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
|
@ -5,24 +5,23 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.handoff.attributes.text
|
(ns app.main.ui.handoff.attributes.text
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[app.common.text :as txt]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
|
[app.main.store :as st]
|
||||||
|
[app.main.ui.components.copy-button :refer [copy-button]]
|
||||||
|
[app.main.ui.handoff.attributes.common :refer [color-row]]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
|
[app.util.i18n :refer [tr]]
|
||||||
|
[app.util.code-gen :as cg]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.webapi :as wapi]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[app.util.data :as d]
|
[rumext.alpha :as mf]))
|
||||||
[app.util.i18n :refer [t]]
|
|
||||||
[app.util.color :as uc]
|
|
||||||
[app.util.text :as ut]
|
|
||||||
[app.main.fonts :as fonts]
|
|
||||||
[app.main.ui.icons :as i]
|
|
||||||
[app.util.webapi :as wapi]
|
|
||||||
[app.main.ui.handoff.attributes.common :refer [color-row]]
|
|
||||||
[app.util.code-gen :as cg]
|
|
||||||
[app.main.store :as st]
|
|
||||||
[app.main.ui.components.copy-button :refer [copy-button]]))
|
|
||||||
|
|
||||||
(defn has-text? [shape]
|
(defn has-text? [shape]
|
||||||
(:content shape))
|
(:content shape))
|
||||||
|
@ -72,7 +71,7 @@
|
||||||
([style & properties]
|
([style & properties]
|
||||||
(cg/generate-css-props style properties params)))
|
(cg/generate-css-props style properties params)))
|
||||||
|
|
||||||
(mf/defc typography-block [{:keys [shape locale text style full-style]}]
|
(mf/defc typography-block [{:keys [shape text style full-style]}]
|
||||||
(let [typography-library-ref (mf/use-memo
|
(let [typography-library-ref (mf/use-memo
|
||||||
(mf/deps (:typography-ref-file style))
|
(mf/deps (:typography-ref-file style))
|
||||||
(make-typographies-library-ref (:typography-ref-file style)))
|
(make-typographies-library-ref (:typography-ref-file style)))
|
||||||
|
@ -93,7 +92,7 @@
|
||||||
{:style {:font-family (:font-family typography)
|
{:style {:font-family (:font-family typography)
|
||||||
:font-weight (:font-weight typography)
|
:font-weight (:font-weight typography)
|
||||||
:font-style (:font-style typography)}}
|
:font-style (:font-style typography)}}
|
||||||
(t locale "workspace.assets.typography.sample")]]
|
(tr "workspace.assets.typography.sample")]]
|
||||||
[:div.typography-entry-name (:name typography)]
|
[:div.typography-entry-name (:name typography)]
|
||||||
[:& copy-button {:data (copy-style-data typography)}]]
|
[:& copy-button {:data (copy-style-data typography)}]]
|
||||||
|
|
||||||
|
@ -102,7 +101,7 @@
|
||||||
{:style {:font-family (:font-family full-style)
|
{:style {:font-family (:font-family full-style)
|
||||||
:font-weight (:font-weight full-style)
|
:font-weight (:font-weight full-style)
|
||||||
:font-style (:font-style full-style)}}
|
:font-style (:font-style full-style)}}
|
||||||
(t locale "workspace.assets.typography.sample")]
|
(tr "workspace.assets.typography.sample")]
|
||||||
[:& copy-button {:data (copy-style-data style)}]])
|
[:& copy-button {:data (copy-style-data style)}]])
|
||||||
|
|
||||||
[:div.attributes-content-row
|
[:div.attributes-content-row
|
||||||
|
@ -117,78 +116,83 @@
|
||||||
|
|
||||||
(when (:font-id style)
|
(when (:font-id style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-family")]
|
[:div.attributes-label (tr "handoff.attributes.typography.font-family")]
|
||||||
[:div.attributes-value (-> style :font-id fonts/get-font-data :name)]
|
[:div.attributes-value (-> style :font-id fonts/get-font-data :name)]
|
||||||
[:& copy-button {:data (copy-style-data style :font-family)}]])
|
[:& copy-button {:data (copy-style-data style :font-family)}]])
|
||||||
|
|
||||||
(when (:font-style style)
|
(when (:font-style style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-style")]
|
[:div.attributes-label (tr "handoff.attributes.typography.font-style")]
|
||||||
[:div.attributes-value (str (:font-style style))]
|
[:div.attributes-value (str (:font-style style))]
|
||||||
[:& copy-button {:data (copy-style-data style :font-style)}]])
|
[:& copy-button {:data (copy-style-data style :font-style)}]])
|
||||||
|
|
||||||
(when (:font-size style)
|
(when (:font-size style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (t locale "handoff.attributes.typography.font-size")]
|
[:div.attributes-label (tr "handoff.attributes.typography.font-size")]
|
||||||
[:div.attributes-value (str (:font-size style)) "px"]
|
[:div.attributes-value (str (:font-size style)) "px"]
|
||||||
[:& copy-button {:data (copy-style-data style :font-size)}]])
|
[:& copy-button {:data (copy-style-data style :font-size)}]])
|
||||||
|
|
||||||
(when (:line-height style)
|
(when (:line-height style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (t locale "handoff.attributes.typography.line-height")]
|
[:div.attributes-label (tr "handoff.attributes.typography.line-height")]
|
||||||
[:div.attributes-value (str (:line-height style)) "px"]
|
[:div.attributes-value (str (:line-height style)) "px"]
|
||||||
[:& copy-button {:data (copy-style-data style :line-height)}]])
|
[:& copy-button {:data (copy-style-data style :line-height)}]])
|
||||||
|
|
||||||
(when (:letter-spacing style)
|
(when (:letter-spacing style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (t locale "handoff.attributes.typography.letter-spacing")]
|
[:div.attributes-label (tr "handoff.attributes.typography.letter-spacing")]
|
||||||
[:div.attributes-value (str (:letter-spacing style)) "px"]
|
[:div.attributes-value (str (:letter-spacing style)) "px"]
|
||||||
[:& copy-button {:data (copy-style-data style :letter-spacing)}]])
|
[:& copy-button {:data (copy-style-data style :letter-spacing)}]])
|
||||||
|
|
||||||
(when (:text-decoration style)
|
(when (:text-decoration style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-decoration")]
|
[:div.attributes-label (tr "handoff.attributes.typography.text-decoration")]
|
||||||
[:div.attributes-value (->> style :text-decoration (str "handoff.attributes.typography.text-decoration.") (t locale))]
|
[:div.attributes-value (->> style :text-decoration (str "handoff.attributes.typography.text-decoration.") (tr))]
|
||||||
[:& copy-button {:data (copy-style-data style :text-decoration)}]])
|
[:& copy-button {:data (copy-style-data style :text-decoration)}]])
|
||||||
|
|
||||||
(when (:text-transform style)
|
(when (:text-transform style)
|
||||||
[:div.attributes-unit-row
|
[:div.attributes-unit-row
|
||||||
[:div.attributes-label (t locale "handoff.attributes.typography.text-transform")]
|
[:div.attributes-label (tr "handoff.attributes.typography.text-transform")]
|
||||||
[:div.attributes-value (->> style :text-transform (str "handoff.attributes.typography.text-transform.") (t locale))]
|
[:div.attributes-value (->> style :text-transform (str "handoff.attributes.typography.text-transform.") (tr))]
|
||||||
[:& copy-button {:data (copy-style-data style :text-transform)}]])]))
|
[:& copy-button {:data (copy-style-data style :text-transform)}]])]))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc text-block [{:keys [shape locale]}]
|
(defn- remove-equal-values
|
||||||
(let [font (ut/search-text-attrs (:content shape)
|
[m1 m2]
|
||||||
(keys ut/default-text-attrs))
|
(if (and (map? m1) (map? m2) (not (nil? m1)) (not (nil? m2)))
|
||||||
style-text-blocks (->> (keys ut/default-text-attrs)
|
(->> m1
|
||||||
(ut/parse-style-text-blocks (:content shape))
|
(remove (fn [[k v]] (= (k m2) v)))
|
||||||
(remove (fn [[style text]] (str/empty? (str/trim text))))
|
(into {}))
|
||||||
(mapv (fn [[style text]] (vector (merge ut/default-text-attrs style) text))))
|
m1))
|
||||||
|
|
||||||
font (merge ut/default-text-attrs font)]
|
(mf/defc text-block [{:keys [shape]}]
|
||||||
|
(let [font (cg/search-text-attrs (:content shape)
|
||||||
|
(keys txt/default-text-attrs))
|
||||||
|
style-text-blocks (->> (keys txt/default-text-attrs)
|
||||||
|
(cg/parse-style-text-blocks (:content shape))
|
||||||
|
(remove (fn [[style text]] (str/empty? (str/trim text))))
|
||||||
|
(mapv (fn [[style text]] (vector (merge txt/default-text-attrs style) text))))
|
||||||
|
|
||||||
|
font (merge txt/default-text-attrs font)]
|
||||||
(for [[idx [full-style text]] (map-indexed vector style-text-blocks)]
|
(for [[idx [full-style text]] (map-indexed vector style-text-blocks)]
|
||||||
(let [previus-style (first (nth style-text-blocks (dec idx) nil))
|
(let [previus-style (first (nth style-text-blocks (dec idx) nil))
|
||||||
style (d/remove-equal-values full-style previus-style)
|
style (remove-equal-values full-style previus-style)
|
||||||
|
|
||||||
;; If the color is set we need to add opacity otherwise the display will not work
|
;; If the color is set we need to add opacity otherwise the display will not work
|
||||||
style (cond-> style
|
style (cond-> style
|
||||||
(:fill-color style)
|
(:fill-color style)
|
||||||
(assoc :fill-opacity (:fill-opacity full-style)))]
|
(assoc :fill-opacity (:fill-opacity full-style)))]
|
||||||
[:& typography-block {:shape shape
|
[:& typography-block {:shape shape
|
||||||
:locale locale
|
|
||||||
:full-style full-style
|
:full-style full-style
|
||||||
:style style
|
:style style
|
||||||
:text text}]))))
|
:text text}]))))
|
||||||
|
|
||||||
(mf/defc text-panel [{:keys [shapes locale]}]
|
(mf/defc text-panel
|
||||||
(let [shapes (->> shapes (filter has-text?))]
|
[{:keys [shapes]}]
|
||||||
(when (seq shapes)
|
(when-let [shapes (seq (filter has-text? shapes))]
|
||||||
[:div.attributes-block
|
[:div.attributes-block
|
||||||
[:div.attributes-block-title
|
[:div.attributes-block-title
|
||||||
[:div.attributes-block-title-text (t locale "handoff.attributes.typography")]]
|
[:div.attributes-block-title-text (tr "handoff.attributes.typography")]]
|
||||||
|
|
||||||
(for [shape shapes]
|
|
||||||
[:& text-block {:shape shape
|
|
||||||
:locale locale}])])))
|
|
||||||
|
|
||||||
|
(for [shape shapes]
|
||||||
|
[:& text-block {:shape shape}])]))
|
||||||
|
|
|
@ -5,103 +5,86 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.shapes.text
|
(ns app.main.ui.shapes.text
|
||||||
(:require
|
(:require
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.alpha :as mf]
|
|
||||||
[app.main.ui.context :as muc]
|
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.main.ui.context :as muc]
|
||||||
[app.util.object :as obj]
|
|
||||||
[app.util.color :as uc]
|
|
||||||
[app.main.ui.shapes.text.styles :as sts]
|
|
||||||
[app.main.ui.shapes.text.embed :as ste]
|
[app.main.ui.shapes.text.embed :as ste]
|
||||||
[app.util.perf :as perf]))
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
|
[app.util.color :as uc]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(mf/defc render-text
|
(mf/defc render-text
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
text (:text node)
|
text (:text node)
|
||||||
style (sts/generate-text-styles props)]
|
style (sts/generate-text-styles node)]
|
||||||
[:span {:style style
|
[:span {:style style}
|
||||||
:className (when (:fill-color-gradient node) "gradient")}
|
|
||||||
(if (= text "") "\u00A0" text)]))
|
(if (= text "") "\u00A0" text)]))
|
||||||
|
|
||||||
(mf/defc render-root
|
(mf/defc render-root
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
embed-fonts? (obj/get props "embed-fonts?")
|
embed? (obj/get props "embed-fonts?")
|
||||||
children (obj/get props "children")
|
children (obj/get props "children")
|
||||||
style (sts/generate-root-styles props)]
|
shape (obj/get props "shape")
|
||||||
|
style (sts/generate-root-styles shape node)]
|
||||||
[:div.root.rich-text
|
[:div.root.rich-text
|
||||||
{:style style
|
{:style style
|
||||||
:xmlns "http://www.w3.org/1999/xhtml"}
|
:xmlns "http://www.w3.org/1999/xhtml"}
|
||||||
[:*
|
(when embed?
|
||||||
[:style ".gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
[ste/embed-fontfaces-style {:node node}])
|
||||||
(when embed-fonts?
|
|
||||||
[ste/embed-fontfaces-style {:node node}])]
|
|
||||||
children]))
|
children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph-set
|
(mf/defc render-paragraph-set
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
children (obj/get props "children")
|
children (obj/get props "children")
|
||||||
style (sts/generate-paragraph-set-styles props)]
|
shape (obj/get props "shape")
|
||||||
|
style (sts/generate-paragraph-set-styles shape)]
|
||||||
[:div.paragraph-set {:style style} children]))
|
[:div.paragraph-set {:style style} children]))
|
||||||
|
|
||||||
(mf/defc render-paragraph
|
(mf/defc render-paragraph
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [node (obj/get props "node")
|
(let [node (obj/get props "node")
|
||||||
|
shape (obj/get props "shape")
|
||||||
children (obj/get props "children")
|
children (obj/get props "children")
|
||||||
style (sts/generate-paragraph-styles props)]
|
style (sts/generate-paragraph-styles shape node)]
|
||||||
[:p.paragraph {:style style} children]))
|
[:p.paragraph {:style style :dir "auto"} children]))
|
||||||
|
|
||||||
;; -- Text nodes
|
;; -- Text nodes
|
||||||
(mf/defc render-node
|
(mf/defc render-node
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [node (obj/get props "node")
|
(let [{:keys [type text children] :as node} (obj/get props "node")]
|
||||||
index (obj/get props "index")
|
|
||||||
{:keys [type text children]} node]
|
|
||||||
(if (string? text)
|
(if (string? text)
|
||||||
[:> render-text props]
|
[:> render-text props]
|
||||||
|
|
||||||
(let [component (case type
|
(let [component (case type
|
||||||
"root" render-root
|
"root" render-root
|
||||||
"paragraph-set" render-paragraph-set
|
"paragraph-set" render-paragraph-set
|
||||||
"paragraph" render-paragraph
|
"paragraph" render-paragraph
|
||||||
nil)]
|
nil)]
|
||||||
(when component
|
(when component
|
||||||
[:> component (obj/set! props "key" index)
|
[:> component props
|
||||||
(for [[index child] (d/enumerate children)]
|
(for [[index node] (d/enumerate children)]
|
||||||
(let [props (-> (obj/clone props)
|
(let [props (-> (obj/clone props)
|
||||||
(obj/set! "node" child)
|
(obj/set! "node" node)
|
||||||
(obj/set! "index" index)
|
(obj/set! "index" index)
|
||||||
(obj/set! "key" index))]
|
(obj/set! "key" index))]
|
||||||
[:> render-node props]))])))))
|
[:> render-node props]))])))))
|
||||||
|
|
||||||
(mf/defc text-content
|
|
||||||
{::mf/wrap-props false}
|
|
||||||
[props]
|
|
||||||
(let [root (obj/get props "content")
|
|
||||||
shape (obj/get props "shape")
|
|
||||||
embed-fonts? (obj/get props "embed-fonts?")]
|
|
||||||
[:& render-node {:index 0
|
|
||||||
:node root
|
|
||||||
:shape shape
|
|
||||||
:embed-fonts? embed-fonts?}]))
|
|
||||||
|
|
||||||
(defn- retrieve-colors
|
(defn- retrieve-colors
|
||||||
[shape]
|
[shape]
|
||||||
(let [colors (->> shape
|
(let [colors (->> (:content shape)
|
||||||
:content
|
|
||||||
(tree-seq map? :children)
|
(tree-seq map? :children)
|
||||||
(into #{} (comp (map :fill-color) (filter string?))))]
|
(into #{} (comp (map :fill-color) (filter string?))))]
|
||||||
(if (empty? colors)
|
(if (empty? colors)
|
||||||
|
@ -112,20 +95,20 @@
|
||||||
{::mf/wrap-props false
|
{::mf/wrap-props false
|
||||||
::mf/forward-ref true}
|
::mf/forward-ref true}
|
||||||
[props ref]
|
[props ref]
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [{:keys [id x y width height content grow-type] :as shape} (obj/get props "shape")
|
||||||
grow-type (unchecked-get props "grow-type")
|
|
||||||
embed-fonts? (mf/use-ctx muc/embed-ctx)
|
embed-fonts? (mf/use-ctx muc/embed-ctx)
|
||||||
{:keys [id x y width height content]} shape
|
|
||||||
;; We add 8px to add a padding for the exporter
|
;; We add 8px to add a padding for the exporter
|
||||||
width (+ width 8)]
|
;; width (+ width 8)
|
||||||
|
]
|
||||||
[:foreignObject {:x x
|
[:foreignObject {:x x
|
||||||
:y y
|
:y y
|
||||||
:id (:id shape)
|
:id id
|
||||||
:data-colors (retrieve-colors shape)
|
:data-colors (retrieve-colors shape)
|
||||||
:transform (geom/transform-matrix shape)
|
:transform (geom/transform-matrix shape)
|
||||||
:width (if (#{:auto-width} grow-type) 100000 width)
|
:width (if (#{:auto-width} grow-type) 100000 width)
|
||||||
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)
|
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)
|
||||||
:ref ref}
|
:ref ref}
|
||||||
[:& text-content {:shape shape
|
[:& render-node {:index 0
|
||||||
:content (:content shape)
|
:shape shape
|
||||||
:embed-fonts? embed-fonts?}]]))
|
:node content
|
||||||
|
:embed-fonts? embed-fonts?}]]))
|
||||||
|
|
|
@ -5,43 +5,46 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.shapes.text.embed
|
(ns app.main.ui.shapes.text.embed
|
||||||
(:require
|
(:require
|
||||||
[clojure.set :as set]
|
[app.common.data :as d]
|
||||||
[promesa.core :as p]
|
[app.common.text :as txt]
|
||||||
[cuerdas.core :as str]
|
|
||||||
[rumext.alpha :as mf]
|
|
||||||
[app.main.data.fetch :as df]
|
[app.main.data.fetch :as df]
|
||||||
[app.main.fonts :as fonts]
|
[app.main.fonts :as fonts]
|
||||||
[app.util.text :as ut]))
|
[app.util.object :as obj]
|
||||||
|
[clojure.set :as set]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[promesa.core :as p]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(defonce font-face-template "
|
(def font-face-template "
|
||||||
/* latin */
|
/* latin */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: '$0';
|
font-family: '%(family)s';
|
||||||
font-style: $3;
|
font-style: %(style)s;
|
||||||
font-weight: $2;
|
font-weight: %(weight)s;
|
||||||
font-display: block;
|
font-display: block;
|
||||||
src: url(/fonts/%(0)s-$1.woff) format('woff');
|
src: url(/fonts/%(family)s-%(style)s.woff) format('woff');
|
||||||
}
|
}
|
||||||
")
|
")
|
||||||
|
|
||||||
;; -- Embed fonts into styles
|
;; -- Embed fonts into styles
|
||||||
(defn get-node-fonts [node]
|
(defn get-node-fonts
|
||||||
|
[node]
|
||||||
(let [current-font (if (not (nil? (:font-id node)))
|
(let [current-font (if (not (nil? (:font-id node)))
|
||||||
#{(select-keys node [:font-id :font-variant-id])}
|
#{(select-keys node [:font-id :font-variant-id])}
|
||||||
#{})
|
#{})
|
||||||
children-font (map get-node-fonts (:children node))]
|
children-font (map get-node-fonts (:children node))]
|
||||||
(reduce set/union (conj children-font current-font))))
|
(reduce set/union (conj children-font current-font))))
|
||||||
|
|
||||||
|
(defn get-local-font-css
|
||||||
(defn get-local-font-css [font-id font-variant-id]
|
[font-id font-variant-id]
|
||||||
(let [{:keys [family variants]} (get @fonts/fontsdb font-id)
|
(let [{:keys [family variants] :as font} (get @fonts/fontsdb font-id)
|
||||||
{:keys [name weight style]} (->> variants (filter #(= (:id %) font-variant-id)) first)
|
{:keys [name weight style] :as variant} (d/seek #(= (:id %) font-variant-id) variants)]
|
||||||
css-str (str/format font-face-template [family name weight style])]
|
(-> (str/format font-face-template {:family family :style style :width weight})
|
||||||
(p/resolved css-str)))
|
(p/resolved))))
|
||||||
|
|
||||||
(defn get-text-font-data [text]
|
(defn get-text-font-data [text]
|
||||||
(->> text
|
(->> text
|
||||||
|
@ -59,17 +62,19 @@
|
||||||
replace-text (fn [text [url data]] (str/replace text url data))]
|
replace-text (fn [text [url data]] (str/replace text url data))]
|
||||||
(reduce replace-text font-text url-to-data))))
|
(reduce replace-text font-text url-to-data))))
|
||||||
|
|
||||||
(mf/defc embed-fontfaces-style [{:keys [node]}]
|
(mf/defc embed-fontfaces-style
|
||||||
(let [embeded-fonts (mf/use-state nil)]
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [node (obj/get props "node")
|
||||||
|
style (mf/use-state nil)]
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps node)
|
(mf/deps node)
|
||||||
(fn []
|
(fn []
|
||||||
(let [font-to-embed (get-node-fonts node)
|
(let [font-to-embed (get-node-fonts node)
|
||||||
font-to-embed (if (empty? font-to-embed) #{ut/default-text-attrs} font-to-embed)
|
font-to-embed (if (empty? font-to-embed) #{txt/default-text-attrs} font-to-embed)
|
||||||
embeded (map embed-font font-to-embed)]
|
embeded (map embed-font font-to-embed)]
|
||||||
(-> (p/all embeded)
|
(-> (p/all embeded)
|
||||||
(p/then (fn [result] (reset! embeded-fonts (str/join "\n" result))))))))
|
(p/then (fn [result] (reset! style (str/join "\n" result))))))))
|
||||||
|
|
||||||
|
(when (some? @style)
|
||||||
(when (not (nil? @embeded-fonts))
|
[:style @style])))
|
||||||
[:style @embeded-fonts])))
|
|
||||||
|
|
|
@ -5,135 +5,120 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.shapes.text.styles
|
(ns app.main.ui.shapes.text.styles
|
||||||
(:require
|
(:require
|
||||||
[cuerdas.core :as str]
|
|
||||||
[app.main.fonts :as fonts]
|
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.util.object :as obj]
|
[app.common.text :as txt]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
[app.util.color :as uc]
|
[app.util.color :as uc]
|
||||||
[app.util.text :as ut]))
|
[app.util.object :as obj]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
(defn generate-root-styles
|
(defn generate-root-styles
|
||||||
([props] (generate-root-styles (clj->js (obj/get props "node")) props))
|
[shape node]
|
||||||
([data props]
|
(let [valign (or (:vertical-align node "top"))
|
||||||
(let [valign (obj/get data "vertical-align" "top")
|
base #js {:height (or (:height shape) "100%")
|
||||||
shape (obj/get props "shape")
|
:width (or (:width shape) "100%")}]
|
||||||
base #js {:height (or (:height shape) "100%")
|
(cond-> base
|
||||||
:width (or (:width shape) "100%")}]
|
(= valign "top") (obj/set! "justifyContent" "flex-start")
|
||||||
(cond-> base
|
(= valign "center") (obj/set! "justifyContent" "center")
|
||||||
(= valign "top") (obj/set! "justifyContent" "flex-start")
|
(= valign "bottom") (obj/set! "justifyContent" "flex-end"))))
|
||||||
(= valign "center") (obj/set! "justifyContent" "center")
|
|
||||||
(= valign "bottom") (obj/set! "justifyContent" "flex-end")
|
|
||||||
))))
|
|
||||||
|
|
||||||
(defn generate-paragraph-set-styles
|
(defn generate-paragraph-set-styles
|
||||||
([props] (generate-paragraph-set-styles (clj->js (obj/get props "node")) props))
|
[{:keys [grow-type] :as shape}]
|
||||||
([data props]
|
;; This element will control the auto-width/auto-height size for the
|
||||||
;; This element will control the auto-width/auto-height size for the
|
;; shape. The properties try to adjust to the shape and "overflow" if
|
||||||
;; shape. The properties try to adjust to the shape and "overflow" if
|
;; the shape is not big enough.
|
||||||
;; the shape is not big enough.
|
;; We `inherit` the property `justify-content` so it's set by the root where
|
||||||
;; We `inherit` the property `justify-content` so it's set by the root where
|
;; the property it's known.
|
||||||
;; the property it's known.
|
;; `inline-flex` is similar to flex but `overflows` outside the bounds of the
|
||||||
;; `inline-flex` is similar to flex but `overflows` outside the bounds of the
|
;; parent
|
||||||
;; parent
|
(let [auto-width? (= grow-type :auto-width)
|
||||||
(let [shape (obj/get props "shape")
|
auto-height? (= grow-type :auto-height)]
|
||||||
grow-type (:grow-type shape)
|
#js {:display "inline-flex"
|
||||||
auto-width? (= grow-type :auto-width)
|
:flexDirection "column"
|
||||||
auto-height? (= grow-type :auto-height)
|
:justifyContent "inherit"
|
||||||
|
:minHeight (when-not (or auto-width? auto-height?) "100%")
|
||||||
base #js {:display "inline-flex"
|
:minWidth (when-not auto-width? "100%")
|
||||||
:flexDirection "column"
|
:verticalAlign "top"}))
|
||||||
:justifyContent "inherit"
|
|
||||||
:minHeight (when-not (or auto-width? auto-height?) "100%")
|
|
||||||
:minWidth (when-not auto-width? "100%")
|
|
||||||
:verticalAlign "top"}]
|
|
||||||
base)))
|
|
||||||
|
|
||||||
(defn generate-paragraph-styles
|
(defn generate-paragraph-styles
|
||||||
([props] (generate-paragraph-styles (clj->js (obj/get props "node")) props))
|
[shape data]
|
||||||
([data props]
|
(let [line-height (:line-height data)
|
||||||
(let [shape (obj/get props "shape")
|
text-align (:text-align data)
|
||||||
grow-type (:grow-type shape)
|
grow-type (:grow-type shape)
|
||||||
base #js {:fontSize "14px"
|
|
||||||
:margin "inherit"
|
base #js {:fontSize (str (:font-size txt/default-text-attrs) "px")
|
||||||
:lineHeight "1.2"}
|
:lineHeight (:line-height txt/default-text-attrs)
|
||||||
lh (obj/get data "line-height")
|
:margin "inherit"}]
|
||||||
ta (obj/get data "text-align")]
|
(cond-> base
|
||||||
(cond-> base
|
(some? line-height) (obj/set! "lineHeight" line-height)
|
||||||
ta (obj/set! "textAlign" ta)
|
(some? text-align) (obj/set! "textAlign" text-align)
|
||||||
lh (obj/set! "lineHeight" lh)
|
(= grow-type :auto-width) (obj/set! "whiteSpace" "pre"))))
|
||||||
(= grow-type :auto-width) (obj/set! "whiteSpace" "pre")))))
|
|
||||||
|
|
||||||
(defn generate-text-styles
|
(defn generate-text-styles
|
||||||
([props] (generate-text-styles (clj->js (obj/get props "node")) props))
|
[data]
|
||||||
([data props]
|
(let [letter-spacing (:letter-spacing data)
|
||||||
(let [letter-spacing (obj/get data "letter-spacing")
|
text-decoration (:text-decoration data)
|
||||||
text-decoration (obj/get data "text-decoration")
|
text-transform (:text-transform data)
|
||||||
text-transform (obj/get data "text-transform")
|
line-height (:line-height data)
|
||||||
line-height (obj/get data "line-height")
|
|
||||||
|
|
||||||
font-id (obj/get data "font-id" (:font-id ut/default-text-attrs))
|
font-id (:font-id data (:font-id txt/default-text-attrs))
|
||||||
font-variant-id (obj/get data "font-variant-id")
|
font-variant-id (:font-variant-id data)
|
||||||
|
|
||||||
font-family (obj/get data "font-family")
|
font-family (:font-family data)
|
||||||
font-size (obj/get data "font-size")
|
font-size (:font-size data)
|
||||||
|
|
||||||
;; Old properties for backwards compatibility
|
fill-color (:fill-color data)
|
||||||
fill (obj/get data "fill")
|
fill-opacity (:fill-opacity data)
|
||||||
opacity (obj/get data "opacity" 1)
|
|
||||||
|
|
||||||
fill-color (obj/get data "fill-color" fill)
|
;; Uncomment this to allow to remove text colors. This could break the texts that already exist
|
||||||
fill-opacity (obj/get data "fill-opacity" opacity)
|
;;[r g b a] (if (nil? fill-color)
|
||||||
fill-color-gradient (obj/get data "fill-color-gradient" nil)
|
;; [0 0 0 0] ;; Transparent color
|
||||||
fill-color-gradient (when fill-color-gradient
|
;; (uc/hex->rgba fill-color fill-opacity))
|
||||||
(-> (js->clj fill-color-gradient :keywordize-keys true)
|
|
||||||
(update :type keyword)))
|
|
||||||
|
|
||||||
;; Uncomment this to allow to remove text colors. This could break the texts that already exist
|
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
||||||
;;[r g b a] (if (nil? fill-color)
|
text-color (str/format "rgba(%s, %s, %s, %s)" r g b a)
|
||||||
;; [0 0 0 0] ;; Transparent color
|
fontsdb (deref fonts/fontsdb)
|
||||||
;; (uc/hex->rgba fill-color fill-opacity))
|
|
||||||
|
|
||||||
[r g b a] (uc/hex->rgba fill-color fill-opacity)
|
base #js {:textDecoration text-decoration
|
||||||
|
:textTransform text-transform
|
||||||
|
:lineHeight (or line-height "inherit")
|
||||||
|
:color text-color}]
|
||||||
|
|
||||||
text-color (if fill-color-gradient
|
(when-let [gradient (:fill-color-gradient data)]
|
||||||
(uc/gradient->css (js->clj fill-color-gradient))
|
(let [text-color (-> (update gradient :type keyword)
|
||||||
(str/format "rgba(%s, %s, %s, %s)" r g b a))
|
(uc/gradient->css))]
|
||||||
|
(-> base
|
||||||
|
(obj/set! "background" "var(--text-color)")
|
||||||
|
(obj/set! "WebkitTextFillColor" "transparent")
|
||||||
|
(obj/set! "WebkitBackgroundClip" "text")
|
||||||
|
(obj/set! "--text-color" text-color))))
|
||||||
|
|
||||||
fontsdb (deref fonts/fontsdb)
|
(when (and (string? letter-spacing)
|
||||||
|
(pos? (alength letter-spacing)))
|
||||||
|
(obj/set! base "letterSpacing" (str letter-spacing "px")))
|
||||||
|
|
||||||
base #js {:textDecoration text-decoration
|
(when (and (string? font-size)
|
||||||
:textTransform text-transform
|
(pos? (alength font-size)))
|
||||||
:lineHeight (or line-height "inherit")
|
(obj/set! base "fontSize" (str font-size "px")))
|
||||||
:color text-color
|
|
||||||
"--text-color" text-color}]
|
|
||||||
|
|
||||||
(when (and (string? letter-spacing)
|
(when (and (string? font-id)
|
||||||
(pos? (alength letter-spacing)))
|
(pos? (alength font-id)))
|
||||||
(obj/set! base "letterSpacing" (str letter-spacing "px")))
|
(fonts/ensure-loaded! font-id)
|
||||||
|
(let [font (get fontsdb font-id)]
|
||||||
|
(let [font-family (or (:family font)
|
||||||
|
(obj/get data "fontFamily"))
|
||||||
|
font-variant (d/seek #(= font-variant-id (:id %))
|
||||||
|
(:variants font))
|
||||||
|
font-style (or (:style font-variant)
|
||||||
|
(obj/get data "fontStyle"))
|
||||||
|
font-weight (or (:weight font-variant)
|
||||||
|
(obj/get data "fontWeight"))]
|
||||||
|
(obj/set! base "fontFamily" font-family)
|
||||||
|
(obj/set! base "fontStyle" font-style)
|
||||||
|
(obj/set! base "fontWeight" font-weight))))
|
||||||
|
|
||||||
(when (and (string? font-size)
|
base))
|
||||||
(pos? (alength font-size)))
|
|
||||||
(obj/set! base "fontSize" (str font-size "px")))
|
|
||||||
|
|
||||||
(when (and (string? font-id)
|
|
||||||
(pos? (alength font-id)))
|
|
||||||
(fonts/ensure-loaded! font-id)
|
|
||||||
(let [font (get fontsdb font-id)]
|
|
||||||
(let [font-family (or (:family font)
|
|
||||||
(obj/get data "fontFamily"))
|
|
||||||
font-variant (d/seek #(= font-variant-id (:id %))
|
|
||||||
(:variants font))
|
|
||||||
font-style (or (:style font-variant)
|
|
||||||
(obj/get data "fontStyle"))
|
|
||||||
font-weight (or (:weight font-variant)
|
|
||||||
(obj/get data "fontWeight"))]
|
|
||||||
(obj/set! base "fontFamily" font-family)
|
|
||||||
(obj/set! base "fontStyle" font-style)
|
|
||||||
(obj/set! base "fontWeight" font-weight))))
|
|
||||||
|
|
||||||
|
|
||||||
base)))
|
|
||||||
|
|
|
@ -141,7 +141,6 @@
|
||||||
[:& (mf/provider ctx/current-team-id) {:value (:team-id project)}
|
[:& (mf/provider ctx/current-team-id) {:value (:team-id project)}
|
||||||
[:& (mf/provider ctx/current-project-id) {:value (:id project)}
|
[:& (mf/provider ctx/current-project-id) {:value (:id project)}
|
||||||
[:& (mf/provider ctx/current-page-id) {:value page-id}
|
[:& (mf/provider ctx/current-page-id) {:value page-id}
|
||||||
|
|
||||||
[:section#workspace
|
[:section#workspace
|
||||||
[:& header {:file file
|
[:& header {:file file
|
||||||
:page-id page-id
|
:page-id page-id
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.workspace.shapes.text
|
(ns app.main.ui.workspace.shapes.text
|
||||||
(:require
|
(:require
|
||||||
|
@ -26,6 +26,7 @@
|
||||||
[app.util.logging :as log]
|
[app.util.logging :as log]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
|
[app.util.text-editor :as ted]
|
||||||
[beicon.core :as rx]
|
[beicon.core :as rx]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
@ -52,8 +53,17 @@
|
||||||
(mf/defc text-resize-content
|
(mf/defc text-resize-content
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [shape (obj/get props "shape")
|
(let [{:keys [id name x y grow-type] :as shape} (obj/get props "shape")
|
||||||
{:keys [id name x y grow-type]} shape
|
|
||||||
|
state-map (mf/deref refs/workspace-editor-state)
|
||||||
|
editor-state (get state-map id)
|
||||||
|
|
||||||
|
shape (cond-> shape
|
||||||
|
(some? editor-state)
|
||||||
|
(assoc :content (-> editor-state
|
||||||
|
(ted/get-editor-current-content)
|
||||||
|
(ted/export-content))))
|
||||||
|
|
||||||
paragraph-ref (mf/use-state nil)
|
paragraph-ref (mf/use-state nil)
|
||||||
|
|
||||||
handle-resize-text
|
handle-resize-text
|
||||||
|
@ -91,8 +101,7 @@
|
||||||
#(.disconnect observer)))))
|
#(.disconnect observer)))))
|
||||||
|
|
||||||
[:& text/text-shape {:ref text-ref-cb
|
[:& text/text-shape {:ref text-ref-cb
|
||||||
:shape shape
|
:shape shape}]))
|
||||||
:grow-type (:grow-type shape)}]))
|
|
||||||
|
|
||||||
(mf/defc text-wrapper
|
(mf/defc text-wrapper
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
|
@ -118,7 +127,6 @@
|
||||||
[:& text-static-content {:shape shape}]
|
[:& text-static-content {:shape shape}]
|
||||||
[:& text-resize-content {:shape shape}])]
|
[:& text-resize-content {:shape shape}])]
|
||||||
|
|
||||||
|
|
||||||
(when (and (not ghost?) edition?)
|
(when (and (not ghost?) edition?)
|
||||||
[:& editor/text-shape-edit {:key (str "editor" (:id shape))
|
[:& editor/text-shape-edit {:key (str "editor" (:id shape))
|
||||||
:shape shape}])
|
:shape shape}])
|
||||||
|
@ -136,4 +144,3 @@
|
||||||
:on-pointer-out handle-pointer-leave
|
:on-pointer-out handle-pointer-leave
|
||||||
:on-double-click handle-double-click
|
:on-double-click handle-double-click
|
||||||
:transform (gsh/transform-matrix shape)}])]))
|
:transform (gsh/transform-matrix shape)}])]))
|
||||||
|
|
||||||
|
|
|
@ -9,190 +9,96 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.shapes.text.editor
|
(ns app.main.ui.workspace.shapes.text.editor
|
||||||
(:require
|
(:require
|
||||||
["slate" :as slate]
|
["draft-js" :as draft]
|
||||||
["slate-react" :as rslate]
|
|
||||||
[goog.events :as events]
|
|
||||||
[rumext.alpha :as mf]
|
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.util.dom :as dom]
|
|
||||||
[app.util.text :as ut]
|
|
||||||
[app.util.object :as obj]
|
|
||||||
[app.main.refs :as refs]
|
|
||||||
[app.main.store :as st]
|
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
|
[app.main.data.workspace.texts :as dwt]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.store :as st]
|
||||||
[app.main.ui.cursors :as cur]
|
[app.main.ui.cursors :as cur]
|
||||||
[app.main.ui.shapes.text.styles :as sts])
|
[app.main.ui.shapes.text.styles :as sts]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.util.text-editor :as ted]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[goog.events :as events]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[rumext.alpha :as mf])
|
||||||
(:import
|
(:import
|
||||||
goog.events.EventType
|
goog.events.EventType
|
||||||
goog.events.KeyCodes))
|
goog.events.KeyCodes))
|
||||||
|
|
||||||
;; --- Data functions
|
;; --- Data functions
|
||||||
|
|
||||||
(defn- initial-text
|
;; TODO: why we need this?
|
||||||
[text]
|
;; (defn- fix-gradients
|
||||||
(clj->js
|
;; "Fix for the gradient types that need to be keywords"
|
||||||
[{:type "root"
|
;; [content]
|
||||||
:children [{:type "paragraph-set"
|
;; (let [fix-node
|
||||||
:children [{:type "paragraph"
|
;; (fn [node]
|
||||||
:children [{:fill-color "#000000"
|
;; (d/update-in-when node [:fill-color-gradient :type] keyword))]
|
||||||
:fill-opacity 1
|
;; (txt/map-node fix-node content)))
|
||||||
:text (or text "")}]}]}]}]))
|
|
||||||
(defn- parse-content
|
|
||||||
[content]
|
|
||||||
(cond
|
|
||||||
(string? content) (initial-text content)
|
|
||||||
(map? content) (clj->js [content])
|
|
||||||
:else (initial-text "")))
|
|
||||||
|
|
||||||
(defn- content-size
|
|
||||||
[node]
|
|
||||||
(let [current (count (:text node))
|
|
||||||
children-count (->> node :children (map content-size) (reduce +))]
|
|
||||||
(+ current children-count)))
|
|
||||||
|
|
||||||
(defn- fix-gradients
|
|
||||||
"Fix for the gradient types that need to be keywords"
|
|
||||||
[content]
|
|
||||||
(let [fix-node
|
|
||||||
(fn [node]
|
|
||||||
(d/update-in-when node [:fill-color-gradient :type] keyword))]
|
|
||||||
(ut/map-node fix-node content)))
|
|
||||||
|
|
||||||
;; --- Text Editor Rendering
|
;; --- Text Editor Rendering
|
||||||
|
|
||||||
(mf/defc editor-root-node
|
(mf/defc block-component
|
||||||
{::mf/wrap-props false
|
|
||||||
::mf/wrap [mf/memo]}
|
|
||||||
[props]
|
|
||||||
(let [
|
|
||||||
childs (obj/get props "children")
|
|
||||||
data (obj/get props "element")
|
|
||||||
type (obj/get data "type")
|
|
||||||
style (sts/generate-root-styles data props)
|
|
||||||
attrs (-> (obj/get props "attributes")
|
|
||||||
(obj/set! "style" style)
|
|
||||||
(obj/set! "className" type))]
|
|
||||||
[:> :div attrs childs]))
|
|
||||||
|
|
||||||
(mf/defc editor-paragraph-set-node
|
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [childs (obj/get props "children")
|
(let [children (obj/get props "children")
|
||||||
data (obj/get props "element")
|
bprops (obj/get props "blockProps")
|
||||||
type (obj/get data "type")
|
style (sts/generate-paragraph-styles (obj/get bprops "shape")
|
||||||
shape (obj/get props "shape")
|
(obj/get bprops "data"))]
|
||||||
style (sts/generate-paragraph-set-styles data props)
|
|
||||||
attrs (-> (obj/get props "attributes")
|
|
||||||
(obj/set! "style" style)
|
|
||||||
(obj/set! "className" type))]
|
|
||||||
[:> :div attrs childs]))
|
|
||||||
|
|
||||||
(mf/defc editor-paragraph-node
|
[:div {:style style :dir "auto"}
|
||||||
{::mf/wrap-props false}
|
[:> draft/EditorBlock props]]))
|
||||||
[props]
|
|
||||||
(let [
|
|
||||||
childs (obj/get props "children")
|
|
||||||
data (obj/get props "element")
|
|
||||||
type (obj/get data "type")
|
|
||||||
style (sts/generate-paragraph-styles data props)
|
|
||||||
attrs (-> (obj/get props "attributes")
|
|
||||||
(obj/set! "style" style)
|
|
||||||
(obj/set! "className" type))]
|
|
||||||
[:> :p attrs childs]))
|
|
||||||
|
|
||||||
(mf/defc editor-text-node
|
(defn render-block
|
||||||
{::mf/wrap-props false}
|
[block shape]
|
||||||
[props]
|
(let [type (ted/get-editor-block-type block)]
|
||||||
(let [childs (obj/get props "children")
|
(case type
|
||||||
data (obj/get props "leaf")
|
"unstyled"
|
||||||
type (obj/get data "type")
|
#js {:editable true
|
||||||
style (sts/generate-text-styles data props)
|
:component block-component
|
||||||
attrs (-> (obj/get props "attributes")
|
:props #js {:data (ted/get-editor-block-data block)
|
||||||
(obj/set! "style" style))
|
:shape shape}}
|
||||||
gradient (obj/get data "fill-color-gradient" nil)]
|
nil)))
|
||||||
(if gradient
|
|
||||||
(obj/set! attrs "className" (str type " gradient"))
|
|
||||||
(obj/set! attrs "className" type))
|
|
||||||
[:> :span attrs childs]))
|
|
||||||
|
|
||||||
(defn- render-element
|
(def empty-editor-state
|
||||||
[shape props]
|
(ted/create-editor-state))
|
||||||
(mf/html
|
|
||||||
(let [element (obj/get props "element")
|
|
||||||
type (obj/get element "type")
|
|
||||||
props (obj/merge! props #js {:shape shape})
|
|
||||||
props (cond-> props
|
|
||||||
(= type "root") (obj/set! "key" "root")
|
|
||||||
(= type "paragraph-set") (obj/set! "key" "paragraph-set"))]
|
|
||||||
|
|
||||||
(case type
|
|
||||||
"root" [:> editor-root-node props]
|
|
||||||
"paragraph-set" [:> editor-paragraph-set-node props]
|
|
||||||
"paragraph" [:> editor-paragraph-node props]
|
|
||||||
nil))))
|
|
||||||
|
|
||||||
(defn- render-text
|
|
||||||
[props]
|
|
||||||
(mf/html
|
|
||||||
[:> editor-text-node props]))
|
|
||||||
|
|
||||||
;; --- Text Shape Edit
|
|
||||||
|
|
||||||
(mf/defc text-shape-edit-html
|
(mf/defc text-shape-edit-html
|
||||||
{::mf/wrap [mf/memo]
|
{::mf/wrap [mf/memo]
|
||||||
::mf/wrap-props false
|
::mf/wrap-props false
|
||||||
::mf/forward-ref true}
|
::mf/forward-ref true}
|
||||||
[props ref]
|
[props ref]
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [{:keys [id x y width height grow-type content] :as shape} (obj/get props "shape")
|
||||||
node-ref (unchecked-get props "node-ref")
|
|
||||||
|
zoom (mf/deref refs/selected-zoom)
|
||||||
|
state-map (mf/deref refs/workspace-editor-state)
|
||||||
|
state (get state-map id empty-editor-state)
|
||||||
|
|
||||||
{:keys [id x y width height content grow-type]} shape
|
|
||||||
zoom (mf/deref refs/selected-zoom)
|
|
||||||
state (mf/use-state #(parse-content content))
|
|
||||||
editor (mf/use-memo #(dwt/create-editor))
|
|
||||||
self-ref (mf/use-ref)
|
self-ref (mf/use-ref)
|
||||||
selecting-ref (mf/use-ref)
|
|
||||||
measure-ref (mf/use-ref)
|
|
||||||
|
|
||||||
content-var (mf/use-var content)
|
|
||||||
|
|
||||||
on-close
|
|
||||||
(fn []
|
|
||||||
(st/emit! dw/clear-edition-mode)
|
|
||||||
(when (= 0 (content-size @content-var))
|
|
||||||
(st/emit! (dws/deselect-shape id)
|
|
||||||
(dw/delete-shapes [id]))))
|
|
||||||
|
|
||||||
on-click-outside
|
on-click-outside
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [target (dom/get-target event)
|
(let [target (dom/get-target event)
|
||||||
options (dom/get-element-by-class "element-options")
|
options (dom/get-element-by-class "element-options")
|
||||||
assets (dom/get-element-by-class "assets-bar")
|
assets (dom/get-element-by-class "assets-bar")
|
||||||
cpicker (dom/get-element-by-class "colorpicker-tooltip")
|
cpicker (dom/get-element-by-class "colorpicker-tooltip")
|
||||||
palette (dom/get-element-by-class "color-palette")
|
palette (dom/get-element-by-class "color-palette")
|
||||||
self (mf/ref-val self-ref)
|
|
||||||
selecting? (mf/ref-val selecting-ref)]
|
|
||||||
|
|
||||||
(when-not (or (and options (.contains options target))
|
self (mf/ref-val self-ref)]
|
||||||
(and assets (.contains assets target))
|
(if (or (and options (.contains options target))
|
||||||
(and self (.contains self target))
|
(and assets (.contains assets target))
|
||||||
(and cpicker (.contains cpicker target))
|
(and self (.contains self target))
|
||||||
(and palette (.contains palette target)))
|
(and cpicker (.contains cpicker target))
|
||||||
(if selecting?
|
(and palette (.contains palette target))
|
||||||
(mf/set-ref-val! selecting-ref false)
|
(= "foreignObject" (.-tagName ^js target)))
|
||||||
(on-close)))))
|
(dom/stop-propagation event)
|
||||||
|
(st/emit! dw/clear-edition-mode))))
|
||||||
on-mouse-down
|
|
||||||
(fn [event]
|
|
||||||
(mf/set-ref-val! selecting-ref true))
|
|
||||||
|
|
||||||
on-mouse-up
|
|
||||||
(fn [event]
|
|
||||||
(mf/set-ref-val! selecting-ref false))
|
|
||||||
|
|
||||||
on-key-up
|
on-key-up
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
@ -200,86 +106,71 @@
|
||||||
(when (= (.-keyCode event) 27) ; ESC
|
(when (= (.-keyCode event) 27) ; ESC
|
||||||
(do
|
(do
|
||||||
(st/emit! :interrupt)
|
(st/emit! :interrupt)
|
||||||
(on-close))))
|
(st/emit! dw/clear-edition-mode))))
|
||||||
|
|
||||||
on-mount
|
on-mount
|
||||||
(fn []
|
(fn []
|
||||||
(let [keys [(events/listen js/document EventType.MOUSEDOWN on-click-outside)
|
(let [keys [(events/listen js/document EventType.MOUSEDOWN on-click-outside)
|
||||||
(events/listen js/document EventType.CLICK on-click-outside)
|
(events/listen js/document EventType.CLICK on-click-outside)
|
||||||
(events/listen js/document EventType.KEYUP on-key-up)]]
|
(events/listen js/document EventType.KEYUP on-key-up)]]
|
||||||
(st/emit! (dwt/assign-editor id editor)
|
(st/emit! (dwt/initialize-editor-state shape)
|
||||||
(dwc/start-undo-transaction))
|
(dwt/select-all shape))
|
||||||
|
|
||||||
#(do
|
#(do
|
||||||
(st/emit! (dwt/assign-editor id nil)
|
(st/emit! (dwt/finalize-editor-state shape))
|
||||||
(dwc/commit-undo-transaction))
|
|
||||||
(doseq [key keys]
|
(doseq [key keys]
|
||||||
(events/unlistenByKey key)))))
|
(events/unlistenByKey key)))))
|
||||||
|
|
||||||
on-focus
|
on-blur
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(dwt/editor-select-all! editor))
|
(dom/stop-propagation event)
|
||||||
|
(dom/prevent-default event))
|
||||||
on-composition-start
|
|
||||||
(mf/use-callback
|
|
||||||
(fn [event]
|
|
||||||
(.insertText slate/Editor editor "")))
|
|
||||||
|
|
||||||
on-change
|
on-change
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(fn [val]
|
(fn [val]
|
||||||
(let [content (js->clj val :keywordize-keys true)
|
(st/emit! (dwt/update-editor-state shape val))))
|
||||||
content (first content)
|
|
||||||
content (fix-gradients content)]
|
|
||||||
;; Append timestamp so we can react to cursor change events
|
|
||||||
(st/emit! (dw/update-shape id {:content (assoc content :ts (js->clj (.now js/Date)))}))
|
|
||||||
(reset! state val)
|
|
||||||
(reset! content-var content))))]
|
|
||||||
|
|
||||||
(mf/use-effect on-mount)
|
on-editor
|
||||||
|
(mf/use-callback
|
||||||
|
(fn [editor]
|
||||||
|
(st/emit! (dwt/update-editor editor))
|
||||||
|
(when editor
|
||||||
|
(.focus ^js editor))))
|
||||||
|
|
||||||
(mf/use-effect
|
handle-return
|
||||||
(mf/deps content)
|
(mf/use-callback
|
||||||
(fn []
|
(fn [event state]
|
||||||
(reset! state (parse-content content))
|
(st/emit! (dwt/update-editor-state shape (ted/editor-split-block state)))
|
||||||
(reset! content-var content)))
|
"handled"))
|
||||||
|
]
|
||||||
|
|
||||||
[:div.text-editor {:ref self-ref}
|
(mf/use-layout-effect on-mount)
|
||||||
[:style "span { line-height: inherit; }
|
|
||||||
.gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
|
[:div.text-editor {:ref self-ref
|
||||||
[:> rslate/Slate {:editor editor
|
:class (dom/classnames
|
||||||
:value @state
|
:align-top (= (:vertical-align content "top") "top")
|
||||||
:on-change on-change}
|
:align-center (= (:vertical-align content) "center")
|
||||||
[:> rslate/Editable
|
:align-bottom (= (:vertical-align content) "bottom"))}
|
||||||
{:auto-focus "true"
|
[:> draft/Editor
|
||||||
:spell-check "false"
|
{:on-change on-change
|
||||||
:on-focus on-focus
|
:on-blur on-blur
|
||||||
:class "rich-text"
|
:handle-return handle-return
|
||||||
:style {:cursor cur/text
|
:custom-style-fn (fn [styles _]
|
||||||
:width (:width shape)}
|
(-> (ted/styles-to-attrs styles)
|
||||||
:render-element #(render-element shape %)
|
(sts/generate-text-styles)))
|
||||||
:render-leaf render-text
|
:block-renderer-fn #(render-block % shape)
|
||||||
:on-mouse-up on-mouse-up
|
:ref on-editor
|
||||||
:on-mouse-down on-mouse-down
|
:editor-state state}]]))
|
||||||
:on-blur (fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
;; WARN: monky patch
|
|
||||||
(obj/set! slate/Transforms "deselect" (constantly nil)))
|
|
||||||
:on-composition-start on-composition-start
|
|
||||||
;; :placeholder (when (= :fixed grow-type) "Type some text here...")
|
|
||||||
}]]]))
|
|
||||||
|
|
||||||
(mf/defc text-shape-edit
|
(mf/defc text-shape-edit
|
||||||
{::mf/wrap [mf/memo]
|
{::mf/wrap [mf/memo]
|
||||||
::mf/wrap-props false
|
::mf/wrap-props false
|
||||||
::mf/forward-ref true}
|
::mf/forward-ref true}
|
||||||
[props ref]
|
[props ref]
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [{:keys [id x y width height grow-type] :as shape} (obj/get props "shape")]
|
||||||
{:keys [x y width height grow-type]} shape]
|
|
||||||
[:foreignObject {:transform (gsh/transform-matrix shape)
|
[:foreignObject {:transform (gsh/transform-matrix shape)
|
||||||
:x x :y y
|
:x x :y y
|
||||||
:width (if (#{:auto-width} grow-type) 100000 width)
|
:width (if (#{:auto-width} grow-type) 100000 width)
|
||||||
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)}
|
:height (if (#{:auto-height :auto-width} grow-type) 100000 height)}
|
||||||
|
|
||||||
[:& text-shape-edit-html {:shape shape}]]))
|
[:& text-shape-edit-html {:shape shape :key (str id)}]]))
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.media :as cm]
|
[app.common.media :as cm]
|
||||||
[app.common.pages :as cp]
|
[app.common.pages :as cp]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cfg]
|
[app.config :as cfg]
|
||||||
[app.main.data.colors :as dc]
|
[app.main.data.colors :as dc]
|
||||||
|
@ -38,7 +39,6 @@
|
||||||
[app.util.i18n :as i18n :refer [tr t]]
|
[app.util.i18n :as i18n :refer [tr t]]
|
||||||
[app.util.keyboard :as kbd]
|
[app.util.keyboard :as kbd]
|
||||||
[app.util.router :as rt]
|
[app.util.router :as rt]
|
||||||
[app.util.text :as ut]
|
|
||||||
[app.util.timers :as timers]
|
[app.util.timers :as timers]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
|
@ -431,7 +431,7 @@
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(mf/deps file-id)
|
(mf/deps file-id)
|
||||||
(fn [value opacity]
|
(fn [value opacity]
|
||||||
(st/emit! (dwl/add-typography ut/default-typography))))
|
(st/emit! (dwl/add-typography txt/default-typography))))
|
||||||
|
|
||||||
handle-change
|
handle-change
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
|
|
|
@ -30,8 +30,8 @@
|
||||||
:fill-color-gradient])
|
:fill-color-gradient])
|
||||||
|
|
||||||
(mf/defc fill-menu
|
(mf/defc fill-menu
|
||||||
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "editor" "values"]))]}
|
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values"]))]}
|
||||||
[{:keys [ids type values editor] :as props}]
|
[{:keys [ids type values] :as props}]
|
||||||
(let [locale (mf/deref i18n/locale)
|
(let [locale (mf/deref i18n/locale)
|
||||||
show? (or (not (nil? (:fill-color values)))
|
show? (or (not (nil? (:fill-color values)))
|
||||||
(not (nil? (:fill-color-gradient values))))
|
(not (nil? (:fill-color-gradient values))))
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
|
[app.common.text :as txt]
|
||||||
[app.main.data.workspace.common :as dwc]
|
[app.main.data.workspace.common :as dwc]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.workspace.texts :as dwt]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
|
@ -22,7 +23,6 @@
|
||||||
[app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry typography-options]]
|
[app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry typography-options]]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[app.util.text :as ut]
|
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.alpha :as mf]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@
|
||||||
(def attrs (d/concat #{} shape-attrs root-attrs paragraph-attrs text-attrs))
|
(def attrs (d/concat #{} shape-attrs root-attrs paragraph-attrs text-attrs))
|
||||||
|
|
||||||
(mf/defc text-align-options
|
(mf/defc text-align-options
|
||||||
[{:keys [editor ids values on-change] :as props}]
|
[{:keys [ids values on-change] :as props}]
|
||||||
(let [{:keys [text-align]} values
|
(let [{:keys [text-align]} values
|
||||||
|
|
||||||
text-align (or text-align "left")
|
text-align (or text-align "left")
|
||||||
|
@ -83,7 +83,7 @@
|
||||||
|
|
||||||
|
|
||||||
(mf/defc vertical-align
|
(mf/defc vertical-align
|
||||||
[{:keys [shapes editor ids values on-change] :as props}]
|
[{:keys [shapes ids values on-change] :as props}]
|
||||||
(let [{:keys [vertical-align]} values
|
(let [{:keys [vertical-align]} values
|
||||||
vertical-align (or vertical-align "top")
|
vertical-align (or vertical-align "top")
|
||||||
handle-change
|
handle-change
|
||||||
|
@ -108,7 +108,7 @@
|
||||||
i/align-bottom]]))
|
i/align-bottom]]))
|
||||||
|
|
||||||
(mf/defc grow-options
|
(mf/defc grow-options
|
||||||
[{:keys [editor ids values on-change] :as props}]
|
[{:keys [ids values on-change] :as props}]
|
||||||
(let [to-single-value (fn [coll] (if (> (count coll) 1) nil (first coll)))
|
(let [to-single-value (fn [coll] (if (> (count coll) 1) nil (first coll)))
|
||||||
grow-type (->> values :grow-type)
|
grow-type (->> values :grow-type)
|
||||||
handle-change-grow
|
handle-change-grow
|
||||||
|
@ -133,7 +133,7 @@
|
||||||
i/auto-height]]))
|
i/auto-height]]))
|
||||||
|
|
||||||
(mf/defc text-decoration-options
|
(mf/defc text-decoration-options
|
||||||
[{:keys [editor ids values on-change] :as props}]
|
[{:keys [ids values on-change] :as props}]
|
||||||
(let [{:keys [text-decoration]} values
|
(let [{:keys [text-decoration]} values
|
||||||
|
|
||||||
text-decoration (or text-decoration "none")
|
text-decoration (or text-decoration "none")
|
||||||
|
@ -160,14 +160,14 @@
|
||||||
:on-click #(handle-change % "line-through")}
|
:on-click #(handle-change % "line-through")}
|
||||||
i/strikethrough]]))
|
i/strikethrough]]))
|
||||||
|
|
||||||
(defn generate-typography-name [{:keys [font-id font-variant-id] :as typography}]
|
(defn generate-typography-name
|
||||||
|
[{:keys [font-id font-variant-id] :as typography}]
|
||||||
(let [{:keys [name]} (fonts/get-font-data font-id)]
|
(let [{:keys [name]} (fonts/get-font-data font-id)]
|
||||||
(-> typography
|
(assoc typography :name (str name " " (str/title font-variant-id)))))
|
||||||
(assoc :name (str name " " (str/title font-variant-id))))) )
|
|
||||||
|
|
||||||
(mf/defc text-menu
|
(mf/defc text-menu
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]}
|
||||||
[{:keys [ids type editor values] :as props}]
|
[{:keys [ids type values] :as props}]
|
||||||
|
|
||||||
(let [current-file-id (mf/use-ctx ctx/current-file-id)
|
(let [current-file-id (mf/use-ctx ctx/current-file-id)
|
||||||
typographies (mf/deref refs/workspace-file-typography)
|
typographies (mf/deref refs/workspace-file-typography)
|
||||||
|
@ -181,15 +181,15 @@
|
||||||
(fn [id attrs]
|
(fn [id attrs]
|
||||||
(let [attrs (select-keys attrs root-attrs)]
|
(let [attrs (select-keys attrs root-attrs)]
|
||||||
(when-not (empty? attrs)
|
(when-not (empty? attrs)
|
||||||
(st/emit! (dwt/update-root-attrs {:id id :editor editor :attrs attrs}))))
|
(st/emit! (dwt/update-root-attrs {:id id :attrs attrs}))))
|
||||||
|
|
||||||
(let [attrs (select-keys attrs paragraph-attrs)]
|
(let [attrs (select-keys attrs paragraph-attrs)]
|
||||||
(when-not (empty? attrs)
|
(when-not (empty? attrs)
|
||||||
(st/emit! (dwt/update-paragraph-attrs {:id id :editor editor :attrs attrs}))))
|
(st/emit! (dwt/update-paragraph-attrs {:id id :attrs attrs}))))
|
||||||
|
|
||||||
(let [attrs (select-keys attrs text-attrs)]
|
(let [attrs (select-keys attrs text-attrs)]
|
||||||
(when-not (empty? attrs)
|
(when-not (empty? attrs)
|
||||||
(st/emit! (dwt/update-text-attrs {:id id :editor editor :attrs attrs})))))
|
(st/emit! (dwt/update-text-attrs {:id id :attrs attrs})))))
|
||||||
|
|
||||||
typography (cond
|
typography (cond
|
||||||
(and (:typography-ref-id values)
|
(and (:typography-ref-id values)
|
||||||
|
@ -213,7 +213,7 @@
|
||||||
(d/concat text-font-attrs
|
(d/concat text-font-attrs
|
||||||
text-spacing-attrs
|
text-spacing-attrs
|
||||||
text-transform-attrs)))
|
text-transform-attrs)))
|
||||||
typography (merge ut/default-typography setted-values)
|
typography (merge txt/default-typography setted-values)
|
||||||
typography (generate-typography-name typography)]
|
typography (generate-typography-name typography)]
|
||||||
(let [id (uuid/next)]
|
(let [id (uuid/next)]
|
||||||
(st/emit! (dwl/add-typography (assoc typography :id id) false))
|
(st/emit! (dwl/add-typography (assoc typography :id id) false))
|
||||||
|
@ -230,8 +230,7 @@
|
||||||
(fn [changes]
|
(fn [changes]
|
||||||
(st/emit! (dwl/update-typography (merge typography changes) current-file-id)))
|
(st/emit! (dwl/update-typography (merge typography changes) current-file-id)))
|
||||||
|
|
||||||
opts #js {:editor editor
|
opts #js {:ids ids
|
||||||
:ids ids
|
|
||||||
:values values
|
:values values
|
||||||
:on-change (fn [attrs]
|
:on-change (fn [attrs]
|
||||||
(run! #(emit-update! % attrs) ids))}]
|
(run! #(emit-update! % attrs) ids))}]
|
||||||
|
|
|
@ -5,25 +5,25 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.main.ui.workspace.sidebar.options.menus.typography
|
(ns app.main.ui.workspace.sidebar.options.menus.typography
|
||||||
(:require
|
(:require
|
||||||
[rumext.alpha :as mf]
|
[app.common.data :as d]
|
||||||
[cuerdas.core :as str]
|
[app.common.text :as txt]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.data.workspace.texts :as dwt]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.common.data :as d]
|
|
||||||
[app.main.data.workspace.texts :as dwt]
|
|
||||||
[app.main.ui.components.editable-select :refer [editable-select]]
|
[app.main.ui.components.editable-select :refer [editable-select]]
|
||||||
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
|
[app.main.ui.workspace.sidebar.options.common :refer [advanced-options]]
|
||||||
[app.main.fonts :as fonts]
|
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.text :as ut]
|
|
||||||
[app.util.timers :as ts]
|
|
||||||
[app.util.i18n :as i18n :refer [t]]
|
[app.util.i18n :as i18n :refer [t]]
|
||||||
[app.util.router :as rt]))
|
[app.util.router :as rt]
|
||||||
|
[app.util.timers :as ts]
|
||||||
|
[cuerdas.core :as str]
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
(defn- attr->string [value]
|
(defn- attr->string [value]
|
||||||
(if (= value :multiple)
|
(if (= value :multiple)
|
||||||
|
@ -51,9 +51,9 @@
|
||||||
font-size
|
font-size
|
||||||
font-variant-id]} values
|
font-variant-id]} values
|
||||||
|
|
||||||
font-id (or font-id (:font-id ut/default-text-attrs))
|
font-id (or font-id (:font-id txt/default-text-attrs))
|
||||||
font-size (or font-size (:font-size ut/default-text-attrs))
|
font-size (or font-size (:font-size txt/default-text-attrs))
|
||||||
font-variant-id (or font-variant-id (:font-variant-id ut/default-text-attrs))
|
font-variant-id (or font-variant-id (:font-variant-id txt/default-text-attrs))
|
||||||
|
|
||||||
fonts (mf/deref fonts/fontsdb)
|
fonts (mf/deref fonts/fontsdb)
|
||||||
font (get fonts font-id)
|
font (get fonts font-id)
|
||||||
|
|
|
@ -9,17 +9,17 @@
|
||||||
|
|
||||||
(ns app.main.ui.workspace.sidebar.options.shapes.multiple
|
(ns app.main.ui.workspace.sidebar.options.shapes.multiple
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
|
||||||
[rumext.alpha :as mf]
|
|
||||||
[app.common.attrs :as attrs]
|
[app.common.attrs :as attrs]
|
||||||
[app.util.text :as ut]
|
[app.common.data :as d]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]]
|
[app.common.text :as txt]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
|
||||||
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-attrs shadow-menu]]
|
|
||||||
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs fill-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]]
|
||||||
|
[app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-attrs shadow-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]]
|
[app.main.ui.workspace.sidebar.options.menus.stroke :refer [stroke-attrs stroke-menu]]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.text :as ot]
|
[app.main.ui.workspace.sidebar.options.menus.text :as ot]
|
||||||
[app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]]))
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
;; We define a map that goes from type to
|
;; We define a map that goes from type to
|
||||||
;; attribute and how to handle them
|
;; attribute and how to handle them
|
||||||
|
@ -161,7 +161,7 @@
|
||||||
:text [(conj ids id)
|
:text [(conj ids id)
|
||||||
(-> values
|
(-> values
|
||||||
(merge-attrs (select-keys shape attrs))
|
(merge-attrs (select-keys shape attrs))
|
||||||
(merge-attrs (ut/get-text-attrs-multi content attrs)))]
|
(merge-attrs (attrs/get-attrs-multi (txt/node-seq content) attrs)))]
|
||||||
:children (let [children (->> (:shapes shape []) (map #(get objects %)))
|
:children (let [children (->> (:shapes shape []) (map #(get objects %)))
|
||||||
[new-ids new-values] (get-attrs children objects attr-type)]
|
[new-ids new-values] (get-attrs children objects attr-type)]
|
||||||
[(d/concat ids new-ids) (merge-attrs values new-values)])
|
[(d/concat ids new-ids) (merge-attrs values new-values)])
|
||||||
|
|
|
@ -21,18 +21,16 @@
|
||||||
|
|
||||||
(mf/defc options
|
(mf/defc options
|
||||||
[{:keys [shape] :as props}]
|
[{:keys [shape] :as props}]
|
||||||
(let [ids [(:id shape)]
|
(let [ids [(:id shape)]
|
||||||
type (:type shape)
|
type (:type shape)
|
||||||
|
|
||||||
editors (mf/deref refs/editors)
|
state-map (mf/deref refs/workspace-editor-state)
|
||||||
editor (get editors (:id shape))
|
editor-state (get state-map (:id shape))
|
||||||
|
|
||||||
measure-values (select-keys shape measure-attrs)
|
fill-values (dwt/current-text-values
|
||||||
|
{:editor-state editor-state
|
||||||
fill-values (dwt/current-text-values
|
:shape shape
|
||||||
{:editor editor
|
:attrs text-fill-attrs})
|
||||||
:shape shape
|
|
||||||
:attrs text-fill-attrs})
|
|
||||||
|
|
||||||
fill-values (d/update-in-when fill-values [:fill-color-gradient :type] keyword)
|
fill-values (d/update-in-when fill-values [:fill-color-gradient :type] keyword)
|
||||||
|
|
||||||
|
@ -41,32 +39,42 @@
|
||||||
(:fill fill-values) (assoc :fill-color (:fill fill-values))
|
(:fill fill-values) (assoc :fill-color (:fill fill-values))
|
||||||
(:opacity fill-values) (assoc :fill-opacity (:fill fill-values)))
|
(:opacity fill-values) (assoc :fill-opacity (:fill fill-values)))
|
||||||
|
|
||||||
|
|
||||||
text-values (merge
|
text-values (merge
|
||||||
(select-keys shape [:grow-type])
|
(select-keys shape [:grow-type :vertical-align :text-align])
|
||||||
(dwt/current-root-values
|
#_(dwt/current-root-values
|
||||||
{:editor editor :shape shape
|
{:editor-state editor-state
|
||||||
:attrs root-attrs})
|
:shape shape
|
||||||
(dwt/current-text-values
|
:attrs root-attrs})
|
||||||
{:editor editor :shape shape
|
(dwt/current-paragraph-values
|
||||||
|
{:editor-state editor-state
|
||||||
|
:shape shape
|
||||||
:attrs paragraph-attrs})
|
:attrs paragraph-attrs})
|
||||||
(dwt/current-text-values
|
(dwt/current-text-values
|
||||||
{:editor editor :shape shape
|
{:editor-state editor-state
|
||||||
|
:shape shape
|
||||||
:attrs text-attrs}))]
|
:attrs text-attrs}))]
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
[:& measures-menu {:ids ids
|
|
||||||
:type type
|
[:& measures-menu
|
||||||
:values measure-values}]
|
{:ids ids
|
||||||
[:& fill-menu {:ids ids
|
:type type
|
||||||
:type type
|
:values (select-keys shape measure-attrs)}]
|
||||||
:values fill-values
|
|
||||||
:editor editor}]
|
[:& fill-menu
|
||||||
[:& shadow-menu {:ids ids
|
{:ids ids
|
||||||
:values (select-keys shape [:shadow])}]
|
:type type
|
||||||
[:& blur-menu {:ids ids
|
:values fill-values}]
|
||||||
:values (select-keys shape [:blur])}]
|
|
||||||
[:& text-menu {:ids ids
|
[:& shadow-menu
|
||||||
:type type
|
{:ids ids
|
||||||
:values text-values
|
:values (select-keys shape [:shadow])}]
|
||||||
:editor editor}]]))
|
|
||||||
|
[:& blur-menu
|
||||||
|
{:ids ids
|
||||||
|
:values (select-keys shape [:blur])}]
|
||||||
|
|
||||||
|
[:& text-menu
|
||||||
|
{:ids ids
|
||||||
|
:type type
|
||||||
|
:values text-values}]]))
|
||||||
|
|
|
@ -434,11 +434,13 @@
|
||||||
on-pointer-down
|
on-pointer-down
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [target (dom/get-target event)]
|
(let [target (dom/get-target event)
|
||||||
; Capture mouse pointer to detect the movements even if cursor
|
closest (.closest target ".public-DraftStyleDefault-block")]
|
||||||
; leaves the viewport or the browser itself
|
(when-not closest
|
||||||
; https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
|
;; Capture mouse pointer to detect the movements even if cursor
|
||||||
(.setPointerCapture target (.-pointerId event)))))
|
;; leaves the viewport or the browser itself
|
||||||
|
;; https://developer.mozilla.org/en-US/docs/Web/API/Element/setPointerCapture
|
||||||
|
(.setPointerCapture target (.-pointerId event))))))
|
||||||
|
|
||||||
on-pointer-up
|
on-pointer-up
|
||||||
(mf/use-callback
|
(mf/use-callback
|
||||||
|
|
|
@ -5,14 +5,15 @@
|
||||||
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
;; This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||||
;; defined by the Mozilla Public License, v. 2.0.
|
;; defined by the Mozilla Public License, v. 2.0.
|
||||||
;;
|
;;
|
||||||
;; Copyright (c) 2020 UXBOX Labs SL
|
;; Copyright (c) 2020-2021 UXBOX Labs SL
|
||||||
|
|
||||||
(ns app.util.code-gen
|
(ns app.util.code-gen
|
||||||
(:require
|
(:require
|
||||||
[cuerdas.core :as str]
|
[app.common.data :as d]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.util.text :as ut]
|
[app.common.text :as txt]
|
||||||
[app.util.color :as uc]))
|
[app.util.color :as uc]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
(defn shadow->css [shadow]
|
(defn shadow->css [shadow]
|
||||||
(let [{:keys [style offset-x offset-y blur spread]} shadow
|
(let [{:keys [style offset-x offset-y blur spread]} shadow
|
||||||
|
@ -136,17 +137,55 @@
|
||||||
:format format
|
:format format
|
||||||
:multi multi
|
:multi multi
|
||||||
:tab-size 2})))
|
:tab-size 2})))
|
||||||
|
|
||||||
|
(defn search-text-attrs
|
||||||
|
[node attrs]
|
||||||
|
(->> (txt/node-seq node)
|
||||||
|
(map #(select-keys % attrs))
|
||||||
|
(reduce d/merge)))
|
||||||
|
|
||||||
|
|
||||||
|
;; TODO: used on handoff
|
||||||
|
(defn parse-style-text-blocks
|
||||||
|
[node attrs]
|
||||||
|
(letfn
|
||||||
|
[(rec-style-text-map [acc node style]
|
||||||
|
(let [node-style (merge style (select-keys node attrs))
|
||||||
|
head (or (-> acc first) [{} ""])
|
||||||
|
[head-style head-text] head
|
||||||
|
|
||||||
|
new-acc
|
||||||
|
(cond
|
||||||
|
(:children node)
|
||||||
|
(reduce #(rec-style-text-map %1 %2 node-style) acc (:children node))
|
||||||
|
|
||||||
|
(not= head-style node-style)
|
||||||
|
(cons [node-style (:text node "")] acc)
|
||||||
|
|
||||||
|
:else
|
||||||
|
(cons [node-style (str head-text "" (:text node))] (rest acc)))
|
||||||
|
|
||||||
|
;; We add an end-of-line when finish a paragraph
|
||||||
|
new-acc
|
||||||
|
(if (= (:type node) "paragraph")
|
||||||
|
(let [[hs ht] (first new-acc)]
|
||||||
|
(cons [hs (str ht "\n")] (rest new-acc)))
|
||||||
|
new-acc)]
|
||||||
|
new-acc))]
|
||||||
|
|
||||||
|
(-> (rec-style-text-map [] node {})
|
||||||
|
reverse)))
|
||||||
|
|
||||||
(defn text->properties [shape]
|
(defn text->properties [shape]
|
||||||
(let [text-shape-style (select-keys styles-data [:layout :shadow :blur])
|
(let [text-shape-style (select-keys styles-data [:layout :shadow :blur])
|
||||||
|
|
||||||
shape-props (->> text-shape-style vals (mapcat :props))
|
shape-props (->> text-shape-style vals (mapcat :props))
|
||||||
shape-to-prop (->> text-shape-style vals (map :to-prop) (reduce merge))
|
shape-to-prop (->> text-shape-style vals (map :to-prop) (reduce merge))
|
||||||
shape-format (->> text-shape-style vals (map :format) (reduce merge))
|
shape-format (->> text-shape-style vals (map :format) (reduce merge))
|
||||||
|
|
||||||
|
|
||||||
text-values (->> (ut/search-text-attrs (:content shape) (conj (:props style-text) :fill-color-gradient))
|
text-values (->> (search-text-attrs (:content shape) (conj (:props style-text) :fill-color-gradient))
|
||||||
(merge ut/default-text-attrs))]
|
(d/merge txt/default-text-attrs))]
|
||||||
|
|
||||||
(str/join
|
(str/join
|
||||||
"\n"
|
"\n"
|
||||||
[(generate-css-props shape
|
[(generate-css-props shape
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
(ns app.util.text
|
|
||||||
(:require
|
|
||||||
[cuerdas.core :as str]
|
|
||||||
[app.common.attrs :refer [get-attrs-multi]]))
|
|
||||||
|
|
||||||
(defonce default-text-attrs
|
|
||||||
{:typography-ref-file nil
|
|
||||||
:typography-ref-id nil
|
|
||||||
:font-id "sourcesanspro"
|
|
||||||
:font-family "sourcesanspro"
|
|
||||||
:font-variant-id "regular"
|
|
||||||
:font-size "14"
|
|
||||||
:font-weight "400"
|
|
||||||
:font-style "normal"
|
|
||||||
:line-height "1.2"
|
|
||||||
:letter-spacing "0"
|
|
||||||
:text-transform "none"
|
|
||||||
:text-align "left"
|
|
||||||
:text-decoration "none"
|
|
||||||
:fill-color nil
|
|
||||||
:fill-opacity 1})
|
|
||||||
|
|
||||||
(def typography-fields
|
|
||||||
[:font-id
|
|
||||||
:font-family
|
|
||||||
:font-variant-id
|
|
||||||
:font-size
|
|
||||||
:font-weight
|
|
||||||
:font-style
|
|
||||||
:line-height
|
|
||||||
:letter-spacing
|
|
||||||
:text-transform])
|
|
||||||
|
|
||||||
(def default-typography
|
|
||||||
(merge
|
|
||||||
{:name "Source Sans Pro Regular"}
|
|
||||||
(select-keys default-text-attrs typography-fields)))
|
|
||||||
|
|
||||||
(defn some-node
|
|
||||||
[predicate node]
|
|
||||||
(or (predicate node)
|
|
||||||
(some #(some-node predicate %) (:children node))))
|
|
||||||
|
|
||||||
(defn map-node
|
|
||||||
[map-fn node]
|
|
||||||
(cond-> (map-fn node)
|
|
||||||
(:children node) (update :children (fn [children] (mapv #(map-node map-fn %) children)))))
|
|
||||||
|
|
||||||
(defn content->text
|
|
||||||
[node]
|
|
||||||
(str
|
|
||||||
(if (:children node)
|
|
||||||
(str/join (if (= "paragraph-set" (:type node)) "\n" "") (map content->text (:children node)))
|
|
||||||
(:text node ""))))
|
|
||||||
|
|
||||||
(defn parse-style-text-blocks
|
|
||||||
[node attrs]
|
|
||||||
(letfn
|
|
||||||
[(rec-style-text-map [acc node style]
|
|
||||||
(let [node-style (merge style (select-keys node attrs))
|
|
||||||
head (or (-> acc first) [{} ""])
|
|
||||||
[head-style head-text] head
|
|
||||||
|
|
||||||
new-acc
|
|
||||||
(cond
|
|
||||||
(:children node)
|
|
||||||
(reduce #(rec-style-text-map %1 %2 node-style) acc (:children node))
|
|
||||||
|
|
||||||
(not= head-style node-style)
|
|
||||||
(cons [node-style (:text node "")] acc)
|
|
||||||
|
|
||||||
:else
|
|
||||||
(cons [node-style (str head-text "" (:text node))] (rest acc)))
|
|
||||||
|
|
||||||
;; We add an end-of-line when finish a paragraph
|
|
||||||
new-acc
|
|
||||||
(if (= (:type node) "paragraph")
|
|
||||||
(let [[hs ht] (first new-acc)]
|
|
||||||
(cons [hs (str ht "\n")] (rest new-acc)))
|
|
||||||
new-acc)]
|
|
||||||
new-acc))]
|
|
||||||
|
|
||||||
(-> (rec-style-text-map [] node {})
|
|
||||||
reverse)))
|
|
||||||
|
|
||||||
(defn search-text-attrs
|
|
||||||
[node attrs]
|
|
||||||
(let [rec-fn
|
|
||||||
(fn rec-fn [current node]
|
|
||||||
(let [current (reduce rec-fn current (:children node []))]
|
|
||||||
(merge current
|
|
||||||
(select-keys node attrs))))]
|
|
||||||
(rec-fn {} node)))
|
|
||||||
|
|
||||||
|
|
||||||
(defn content->nodes [node]
|
|
||||||
(loop [result (transient [])
|
|
||||||
curr node
|
|
||||||
pending (transient [])]
|
|
||||||
|
|
||||||
(let [result (conj! result curr)]
|
|
||||||
;; Adds children to the pending list
|
|
||||||
(let [children (:children curr)
|
|
||||||
pending (loop [child (first children)
|
|
||||||
children (rest children)
|
|
||||||
pending pending]
|
|
||||||
(if child
|
|
||||||
(recur (first children)
|
|
||||||
(rest children)
|
|
||||||
(conj! pending child))
|
|
||||||
pending))]
|
|
||||||
|
|
||||||
(if (= 0 (count pending))
|
|
||||||
(persistent! result)
|
|
||||||
;; Iterates with the next value in pending
|
|
||||||
(let [next (get pending (dec (count pending)))]
|
|
||||||
(recur result next (pop! pending))))))))
|
|
||||||
|
|
||||||
(defn get-text-attrs-multi
|
|
||||||
[node attrs]
|
|
||||||
(let [nodes (content->nodes node)]
|
|
||||||
(get-attrs-multi nodes attrs)))
|
|
||||||
|
|
298
frontend/src/app/util/text_editor.cljs
Normal file
298
frontend/src/app/util/text_editor.cljs
Normal file
|
@ -0,0 +1,298 @@
|
||||||
|
;; 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-2021 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.util.text-editor
|
||||||
|
"Draft related abstraction functions."
|
||||||
|
(:require
|
||||||
|
["draft-js" :as draft]
|
||||||
|
[app.common.attrs :as attrs]
|
||||||
|
[app.common.text :as txt]
|
||||||
|
[app.common.data :as d]
|
||||||
|
[app.util.transit :as t]
|
||||||
|
[app.util.array :as arr]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[clojure.walk :as walk]
|
||||||
|
[cuerdas.core :as str]))
|
||||||
|
|
||||||
|
;; --- INLINE STYLES ENCODING
|
||||||
|
|
||||||
|
(defn encode-style-value
|
||||||
|
[v]
|
||||||
|
(cond
|
||||||
|
(string? v) (str "s:" v)
|
||||||
|
(number? v) (str "n:" v)
|
||||||
|
(keyword? v) (str "k:" (name v))
|
||||||
|
(map? v) (str "m:" (t/encode v))
|
||||||
|
|
||||||
|
:else (str "o:" v)))
|
||||||
|
|
||||||
|
(defn decode-style-value
|
||||||
|
[v]
|
||||||
|
(let [prefix (subs v 0 2)]
|
||||||
|
(case prefix
|
||||||
|
"s:" (subs v 2)
|
||||||
|
"n:" (js/Number (subs v 2))
|
||||||
|
"k:" (keyword (subs v 2))
|
||||||
|
"m:" (t/decode (subs v 2))
|
||||||
|
"o:" (subs v 2)
|
||||||
|
v)))
|
||||||
|
|
||||||
|
(defn encode-style
|
||||||
|
[key val]
|
||||||
|
(let [k (d/name key)
|
||||||
|
v (encode-style-value val)]
|
||||||
|
(str "PENPOT$$$" k "$$$" v)))
|
||||||
|
|
||||||
|
(defn attrs-to-styles
|
||||||
|
[attrs]
|
||||||
|
(reduce-kv (fn [res k v]
|
||||||
|
(conj res (encode-style k v)))
|
||||||
|
#{}
|
||||||
|
attrs))
|
||||||
|
|
||||||
|
(defn styles-to-attrs
|
||||||
|
[styles]
|
||||||
|
(persistent!
|
||||||
|
(reduce (fn [result style]
|
||||||
|
(let [[_ k v] (str/split style "$$$" 3)]
|
||||||
|
(assoc! result (keyword k) (decode-style-value v))))
|
||||||
|
(transient {})
|
||||||
|
(seq styles))))
|
||||||
|
|
||||||
|
;; --- CONVERSION
|
||||||
|
|
||||||
|
(defn- parse-draft-styles
|
||||||
|
"Parses draft-js style ranges, converting encoded style name into a
|
||||||
|
key/val pair of data."
|
||||||
|
[styles]
|
||||||
|
(map (fn [item]
|
||||||
|
(let [[_ k v] (-> (obj/get item "style")
|
||||||
|
(str/split "$$$" 3))]
|
||||||
|
{:key (keyword k)
|
||||||
|
:val (decode-style-value v)
|
||||||
|
:offset (obj/get item "offset")
|
||||||
|
:length (obj/get item "length")}))
|
||||||
|
styles))
|
||||||
|
|
||||||
|
(defn- build-style-index
|
||||||
|
"Generates a character based index with associated styles map."
|
||||||
|
[text ranges]
|
||||||
|
(loop [result (->> (range (count text))
|
||||||
|
(mapv (constantly {}))
|
||||||
|
(transient))
|
||||||
|
ranges (seq ranges)]
|
||||||
|
(if-let [{:keys [offset length] :as item} (first ranges)]
|
||||||
|
(recur (reduce (fn [result index]
|
||||||
|
(let [prev (get result index)]
|
||||||
|
(assoc! result index (assoc prev (:key item) (:val item)))))
|
||||||
|
result
|
||||||
|
(range offset (+ offset length)))
|
||||||
|
(rest ranges))
|
||||||
|
(persistent! result))))
|
||||||
|
|
||||||
|
(defn- convert-from-draft
|
||||||
|
[content]
|
||||||
|
(letfn [(build-text [text part]
|
||||||
|
(let [start (ffirst part)
|
||||||
|
end (inc (first (last part)))]
|
||||||
|
(-> (second (first part))
|
||||||
|
(assoc :text (subs text start end)))))
|
||||||
|
|
||||||
|
(split-texts [text styles]
|
||||||
|
(->> (parse-draft-styles styles)
|
||||||
|
(build-style-index text)
|
||||||
|
(d/enumerate)
|
||||||
|
(partition-by second)
|
||||||
|
(mapv #(build-text text %))))
|
||||||
|
|
||||||
|
(build-paragraph [block]
|
||||||
|
(let [key (obj/get block "key")
|
||||||
|
text (obj/get block "text")
|
||||||
|
styles (obj/get block "inlineStyleRanges")
|
||||||
|
data (obj/get block "data")]
|
||||||
|
(-> (js->clj data :keywordize-keys true)
|
||||||
|
(assoc :key key)
|
||||||
|
(assoc :type "paragraph")
|
||||||
|
(assoc :children (split-texts text styles)))))]
|
||||||
|
|
||||||
|
{:type "root"
|
||||||
|
:children
|
||||||
|
[{:type "paragraph-set"
|
||||||
|
:children (->> (obj/get content "blocks")
|
||||||
|
(mapv build-paragraph))}]}))
|
||||||
|
|
||||||
|
(defn- convert-to-draft
|
||||||
|
[root]
|
||||||
|
(letfn [(process-attr [children ranges [k v]]
|
||||||
|
(loop [children (seq children)
|
||||||
|
start nil
|
||||||
|
offset 0
|
||||||
|
ranges ranges]
|
||||||
|
(if-let [{:keys [text] :as item} (first children)]
|
||||||
|
(if (= v (get item k ::novalue))
|
||||||
|
(recur (rest children)
|
||||||
|
(if (nil? start) offset start)
|
||||||
|
(+ offset (alength text))
|
||||||
|
ranges)
|
||||||
|
(if (some? start)
|
||||||
|
(recur (rest children)
|
||||||
|
nil
|
||||||
|
(+ offset (alength text))
|
||||||
|
(arr/conj! ranges #js {:offset start
|
||||||
|
:length (- offset start)
|
||||||
|
:style (encode-style k v)}))
|
||||||
|
(recur (rest children)
|
||||||
|
start
|
||||||
|
(+ offset (alength text))
|
||||||
|
ranges)))
|
||||||
|
(cond-> ranges
|
||||||
|
(some? start)
|
||||||
|
(arr/conj! #js {:offset start
|
||||||
|
:length (- offset start)
|
||||||
|
:style (encode-style k v)})))))
|
||||||
|
|
||||||
|
(calc-ranges [{:keys [children] :as blok}]
|
||||||
|
(let [xform (comp (map #(dissoc % :key :text))
|
||||||
|
(remove empty?)
|
||||||
|
(mapcat vec)
|
||||||
|
(distinct))
|
||||||
|
proc #(process-attr children %1 %2)]
|
||||||
|
(transduce xform proc #js [] children)))
|
||||||
|
|
||||||
|
(build-block [result {:keys [key children] :as paragraph}]
|
||||||
|
(->> #js {:key key
|
||||||
|
:depth 0
|
||||||
|
:text (apply str (map :text children))
|
||||||
|
:data (-> (dissoc paragraph :key :children :type)
|
||||||
|
(clj->js))
|
||||||
|
:type "unstyled"
|
||||||
|
:entityRanges #js []
|
||||||
|
:inlineStyleRanges (calc-ranges paragraph)}
|
||||||
|
(arr/conj! result)))]
|
||||||
|
|
||||||
|
#js {:blocks (reduce build-block #js [] (txt/node-seq #(= (:type %) "paragraph") root))
|
||||||
|
:entityMap #js {}}))
|
||||||
|
|
||||||
|
(defn immutable-map->map
|
||||||
|
[obj]
|
||||||
|
(into {} (map (fn [[k v]] [(keyword k) v])) (seq obj)))
|
||||||
|
|
||||||
|
|
||||||
|
;; --- DRAFT-JS HELPERS
|
||||||
|
|
||||||
|
(defn create-editor-state
|
||||||
|
([]
|
||||||
|
(.createEmpty ^js draft/EditorState))
|
||||||
|
([content]
|
||||||
|
(if (some? content)
|
||||||
|
(.createWithContent ^js draft/EditorState content)
|
||||||
|
(.createEmpty ^js draft/EditorState))))
|
||||||
|
|
||||||
|
(defn import-content
|
||||||
|
[content]
|
||||||
|
(-> content convert-to-draft draft/convertFromRaw))
|
||||||
|
|
||||||
|
(defn export-content
|
||||||
|
[content]
|
||||||
|
(-> content
|
||||||
|
(draft/convertToRaw)
|
||||||
|
(convert-from-draft)))
|
||||||
|
|
||||||
|
(defn get-editor-current-content
|
||||||
|
[state]
|
||||||
|
(.getCurrentContent ^js state))
|
||||||
|
|
||||||
|
(defn ^boolean content-has-text?
|
||||||
|
[content]
|
||||||
|
(.hasText ^js content))
|
||||||
|
|
||||||
|
(defn editor-select-all
|
||||||
|
[state]
|
||||||
|
(let [content (get-editor-current-content state)
|
||||||
|
fblock (.. ^js content getBlockMap first)
|
||||||
|
lblock (.. ^js content getBlockMap last)
|
||||||
|
fbk (.getKey ^js fblock)
|
||||||
|
lbk (.getKey ^js lblock)
|
||||||
|
lbl (.getLength ^js lblock)
|
||||||
|
params #js {:anchorKey fbk
|
||||||
|
:anchorOffset 0
|
||||||
|
:focusKey lbk
|
||||||
|
:focusOffset lbl}
|
||||||
|
selection (draft/SelectionState. params)]
|
||||||
|
(.forceSelection ^js draft/EditorState state selection)))
|
||||||
|
|
||||||
|
(defn get-editor-block-data
|
||||||
|
[block]
|
||||||
|
(-> (.getData ^js block)
|
||||||
|
(immutable-map->map)))
|
||||||
|
|
||||||
|
(defn get-editor-block-type
|
||||||
|
[block]
|
||||||
|
(.getType ^js block))
|
||||||
|
|
||||||
|
(defn get-editor-current-block-data
|
||||||
|
[state]
|
||||||
|
(let [content (.getCurrentContent ^js state)
|
||||||
|
key (.. ^js state getSelection getStartKey)
|
||||||
|
block (.getBlockForKey ^js content key)]
|
||||||
|
(get-editor-block-data block)))
|
||||||
|
|
||||||
|
(defn get-editor-current-inline-styles
|
||||||
|
[state]
|
||||||
|
(-> (.getCurrentInlineStyle ^js state)
|
||||||
|
(styles-to-attrs)))
|
||||||
|
|
||||||
|
(defn update-editor-current-block-data
|
||||||
|
[state attrs]
|
||||||
|
(loop [selection (.getSelection ^js state)
|
||||||
|
start-key (.getStartKey ^js selection)
|
||||||
|
end-key (.getEndKey ^js selection)
|
||||||
|
content (.getCurrentContent ^js state)
|
||||||
|
target selection]
|
||||||
|
(if (and (not= start-key end-key)
|
||||||
|
(zero? (.getEndOffset ^js selection)))
|
||||||
|
(let [before-block (.getBlockBefore ^js content end-key)]
|
||||||
|
(recur selection
|
||||||
|
start-key
|
||||||
|
(.getKey ^js before-block)
|
||||||
|
content
|
||||||
|
(.merge ^js target
|
||||||
|
#js {:anchorKey start-key
|
||||||
|
:anchorOffset (.getStartOffset ^js selection)
|
||||||
|
:focusKey end-key
|
||||||
|
:focusOffset (.getLength ^js before-block)
|
||||||
|
:isBackward false})))
|
||||||
|
(.push ^js draft/EditorState
|
||||||
|
state
|
||||||
|
(.mergeBlockData ^js draft/Modifier content target (clj->js attrs))
|
||||||
|
"change-block-data"))))
|
||||||
|
|
||||||
|
(defn update-editor-current-inline-styles
|
||||||
|
[state attrs]
|
||||||
|
(let [selection (.getSelection ^js state)
|
||||||
|
content (.getCurrentContent ^js state)
|
||||||
|
styles (attrs-to-styles attrs)]
|
||||||
|
(reduce (fn [state style]
|
||||||
|
(let [modifier (.applyInlineStyle draft/Modifier
|
||||||
|
(.getCurrentContent ^js state)
|
||||||
|
selection
|
||||||
|
style)]
|
||||||
|
(.push draft/EditorState state modifier "change-inline-style")))
|
||||||
|
state
|
||||||
|
styles)))
|
||||||
|
|
||||||
|
(defn editor-split-block
|
||||||
|
[state]
|
||||||
|
(let [content (.getCurrentContent ^js state)
|
||||||
|
selection (.getSelection ^js state)
|
||||||
|
content (.splitBlock ^js draft/Modifier content selection)
|
||||||
|
block-data (.. ^js content -blockMap (get (.. content -selectionBefore getStartKey)) getData)
|
||||||
|
block-key (.. ^js content -selectionAfter getStartKey)
|
||||||
|
block-map (.. ^js content -blockMap (update block-key (fn [block] (.set ^js block "data" block-data))))]
|
||||||
|
(.push ^js draft/EditorState state (.set ^js content "blockMap" block-map) "split-block")))
|
|
@ -255,6 +255,11 @@ array-unique@^0.3.2:
|
||||||
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
|
||||||
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
|
||||||
|
|
||||||
|
asap@~2.0.3:
|
||||||
|
version "2.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
|
||||||
|
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||||
|
|
||||||
asn1.js@^5.2.0:
|
asn1.js@^5.2.0:
|
||||||
version "5.4.1"
|
version "5.4.1"
|
||||||
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
|
||||||
|
@ -996,6 +1001,11 @@ core-js-pure@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.9.1.tgz#677b322267172bd490e4464696f790cbc355bec5"
|
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.9.1.tgz#677b322267172bd490e4464696f790cbc355bec5"
|
||||||
integrity sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==
|
integrity sha512-laz3Zx0avrw9a4QEIdmIblnVuJz8W51leY9iLThatCsFawWxC3sE4guASC78JbCin+DkwMpCdp1AVAuzL/GN7A==
|
||||||
|
|
||||||
|
core-js@^3.6.4:
|
||||||
|
version "3.9.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz#cec8de593db8eb2a85ffb0dbdeb312cb6e5460ae"
|
||||||
|
integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg==
|
||||||
|
|
||||||
core-util-is@1.0.2, core-util-is@~1.0.0:
|
core-util-is@1.0.2, core-util-is@~1.0.0:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
|
||||||
|
@ -1042,6 +1052,13 @@ create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7:
|
||||||
safe-buffer "^5.0.1"
|
safe-buffer "^5.0.1"
|
||||||
sha.js "^2.4.8"
|
sha.js "^2.4.8"
|
||||||
|
|
||||||
|
cross-fetch@^3.0.4:
|
||||||
|
version "3.0.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.0.6.tgz#3a4040bc8941e653e0e9cf17f29ebcd177d3365c"
|
||||||
|
integrity sha512-KBPUbqgFjzWlVcURG+Svp9TlhA5uliYtiNx/0r8nv0pdypeQCRJ9IaSIc3q/x3q8t3F75cHuwxVql1HFGHCNJQ==
|
||||||
|
dependencies:
|
||||||
|
node-fetch "2.6.1"
|
||||||
|
|
||||||
cross-spawn@^3.0.0:
|
cross-spawn@^3.0.0:
|
||||||
version "3.0.1"
|
version "3.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
|
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-3.0.1.tgz#1256037ecb9f0c5f79e3d6ef135e30770184b982"
|
||||||
|
@ -1336,6 +1353,15 @@ domutils@^1.7.0:
|
||||||
dom-serializer "0"
|
dom-serializer "0"
|
||||||
domelementtype "1"
|
domelementtype "1"
|
||||||
|
|
||||||
|
draft-js@^0.11.7:
|
||||||
|
version "0.11.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/draft-js/-/draft-js-0.11.7.tgz#be293aaa255c46d8a6647f3860aa4c178484a206"
|
||||||
|
integrity sha512-ne7yFfN4sEL82QPQEn80xnADR8/Q6ALVworbC5UOSzOvjffmYfFsr3xSZtxbIirti14R7Y33EZC5rivpLgIbsg==
|
||||||
|
dependencies:
|
||||||
|
fbjs "^2.0.0"
|
||||||
|
immutable "~3.7.4"
|
||||||
|
object-assign "^4.1.1"
|
||||||
|
|
||||||
duplexify@^3.6.0:
|
duplexify@^3.6.0:
|
||||||
version "3.7.1"
|
version "3.7.1"
|
||||||
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
|
resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309"
|
||||||
|
@ -1661,6 +1687,25 @@ fast-safe-stringify@^2.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743"
|
||||||
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==
|
||||||
|
|
||||||
|
fbjs-css-vars@^1.0.0:
|
||||||
|
version "1.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8"
|
||||||
|
integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==
|
||||||
|
|
||||||
|
fbjs@^2.0.0:
|
||||||
|
version "2.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-2.0.0.tgz#01fb812138d7e31831ed3e374afe27b9169ef442"
|
||||||
|
integrity sha512-8XA8ny9ifxrAWlyhAbexXcs3rRMtxWcs3M0lctLfB49jRDHiaxj+Mo0XxbwE7nKZYzgCFoq64FS+WFd4IycPPQ==
|
||||||
|
dependencies:
|
||||||
|
core-js "^3.6.4"
|
||||||
|
cross-fetch "^3.0.4"
|
||||||
|
fbjs-css-vars "^1.0.0"
|
||||||
|
loose-envify "^1.0.0"
|
||||||
|
object-assign "^4.1.0"
|
||||||
|
promise "^7.1.1"
|
||||||
|
setimmediate "^1.0.5"
|
||||||
|
ua-parser-js "^0.7.18"
|
||||||
|
|
||||||
fd-slicer@~1.1.0:
|
fd-slicer@~1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||||
|
@ -2316,6 +2361,11 @@ immer@^5.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/immer/-/immer-5.3.6.tgz#51eab8cbbeb13075fe2244250f221598818cac04"
|
resolved "https://registry.yarnpkg.com/immer/-/immer-5.3.6.tgz#51eab8cbbeb13075fe2244250f221598818cac04"
|
||||||
integrity sha512-pqWQ6ozVfNOUDjrLfm4Pt7q4Q12cGw2HUZgry4Q5+Myxu9nmHRkWBpI0J4+MK0AxbdFtdMTwEGVl7Vd+vEiK+A==
|
integrity sha512-pqWQ6ozVfNOUDjrLfm4Pt7q4Q12cGw2HUZgry4Q5+Myxu9nmHRkWBpI0J4+MK0AxbdFtdMTwEGVl7Vd+vEiK+A==
|
||||||
|
|
||||||
|
immutable@~3.7.4:
|
||||||
|
version "3.7.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.7.6.tgz#13b4d3cb12befa15482a26fe1b2ebae640071e4b"
|
||||||
|
integrity sha1-E7TTyxK++hVIKib+Gy665kAHHks=
|
||||||
|
|
||||||
import-cwd@^2.0.0:
|
import-cwd@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
||||||
|
@ -3015,7 +3065,7 @@ logform@^2.2.0:
|
||||||
ms "^2.1.1"
|
ms "^2.1.1"
|
||||||
triple-beam "^1.3.0"
|
triple-beam "^1.3.0"
|
||||||
|
|
||||||
loose-envify@^1.1.0:
|
loose-envify@^1.0.0, loose-envify@^1.1.0:
|
||||||
version "1.4.0"
|
version "1.4.0"
|
||||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
|
||||||
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
|
||||||
|
@ -3045,11 +3095,6 @@ lru-queue@^0.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
es5-ext "~0.10.2"
|
es5-ext "~0.10.2"
|
||||||
|
|
||||||
luxon@~1.25.0:
|
|
||||||
version "1.25.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.25.0.tgz#d86219e90bc0102c0eb299d65b2f5e95efe1fe72"
|
|
||||||
integrity sha512-hEgLurSH8kQRjY6i4YLey+mcKVAWXbDNlZRmM6AgWDJ1cY3atl8Ztf5wEY7VBReFbmGnwQPz7KYJblL8B2k0jQ==
|
|
||||||
|
|
||||||
make-iterator@^1.0.0:
|
make-iterator@^1.0.0:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6"
|
resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6"
|
||||||
|
@ -3341,6 +3386,11 @@ nice-try@^1.0.4:
|
||||||
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366"
|
||||||
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==
|
||||||
|
|
||||||
|
node-fetch@2.6.1:
|
||||||
|
version "2.6.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||||
|
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||||
|
|
||||||
node-gyp@^3.8.0:
|
node-gyp@^3.8.0:
|
||||||
version "3.8.0"
|
version "3.8.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
|
resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-3.8.0.tgz#540304261c330e80d0d5edce253a68cb3964218c"
|
||||||
|
@ -3944,6 +3994,13 @@ progress@^1.1.8:
|
||||||
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be"
|
||||||
integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=
|
integrity sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=
|
||||||
|
|
||||||
|
promise@^7.1.1:
|
||||||
|
version "7.3.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
|
||||||
|
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
|
||||||
|
dependencies:
|
||||||
|
asap "~2.0.3"
|
||||||
|
|
||||||
proto-list@~1.2.1:
|
proto-list@~1.2.1:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
|
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
|
||||||
|
@ -4423,7 +4480,7 @@ set-value@^2.0.0, set-value@^2.0.1:
|
||||||
is-plain-object "^2.0.3"
|
is-plain-object "^2.0.3"
|
||||||
split-string "^3.0.1"
|
split-string "^3.0.1"
|
||||||
|
|
||||||
setimmediate@^1.0.4:
|
setimmediate@^1.0.4, setimmediate@^1.0.5:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||||
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
||||||
|
@ -5125,6 +5182,11 @@ typedarray@^0.0.6:
|
||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
|
ua-parser-js@^0.7.18:
|
||||||
|
version "0.7.24"
|
||||||
|
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.24.tgz#8d3ecea46ed4f1f1d63ec25f17d8568105dc027c"
|
||||||
|
integrity sha512-yo+miGzQx5gakzVK3QFfN0/L9uVhosXBBO7qmnk7c2iw1IhL212wfA3zbnI54B0obGwC/5NWub/iT9sReMx+Fw==
|
||||||
|
|
||||||
ultron@~1.1.0:
|
ultron@~1.1.0:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
|
resolved "https://registry.yarnpkg.com/ultron/-/ultron-1.1.1.tgz#9fe1536a10a664a65266a1e3ccf85fd36302bc9c"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue