mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 06:41:38 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
cb6db21e63
50 changed files with 1311 additions and 709 deletions
|
@ -7,6 +7,7 @@
|
||||||
(ns user
|
(ns user
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
|
[app.common.debug :as debug]
|
||||||
[app.common.exceptions :as ex]
|
[app.common.exceptions :as ex]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.fressian :as fres]
|
[app.common.fressian :as fres]
|
||||||
|
@ -55,8 +56,12 @@
|
||||||
[promesa.exec :as px]))
|
[promesa.exec :as px]))
|
||||||
|
|
||||||
(repl/disable-reload! (find-ns 'integrant.core))
|
(repl/disable-reload! (find-ns 'integrant.core))
|
||||||
|
(repl/disable-reload! (find-ns 'app.common.debug))
|
||||||
|
|
||||||
(set! *warn-on-reflection* true)
|
(set! *warn-on-reflection* true)
|
||||||
|
|
||||||
|
(add-tap #'debug/tap-handler)
|
||||||
|
|
||||||
;; --- Benchmarking Tools
|
;; --- Benchmarking Tools
|
||||||
|
|
||||||
(defmacro run-quick-bench
|
(defmacro run-quick-bench
|
||||||
|
@ -132,12 +137,6 @@
|
||||||
;; :v6 v6
|
;; :v6 v6
|
||||||
;; }])))
|
;; }])))
|
||||||
|
|
||||||
(defonce debug-tap
|
|
||||||
(do
|
|
||||||
(add-tap #(locking debug-tap
|
|
||||||
(prn "tap debug:" %)))
|
|
||||||
1))
|
|
||||||
|
|
||||||
|
|
||||||
(defn calculate-frames
|
(defn calculate-frames
|
||||||
[{:keys [data]}]
|
[{:keys [data]}]
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
<Logger name="com.zaxxer.hikari" level="error"/>
|
<Logger name="com.zaxxer.hikari" level="error"/>
|
||||||
<Logger name="org.postgresql" level="error" />
|
<Logger name="org.postgresql" level="error" />
|
||||||
|
|
||||||
<Logger name="app.rpc.commands.binfile" level="debug" />
|
<Logger name="app.binfile" level="debug" />
|
||||||
<Logger name="app.storage.tmp" level="info" />
|
<Logger name="app.storage.tmp" level="info" />
|
||||||
<Logger name="app.worker" level="trace" />
|
<Logger name="app.worker" level="trace" />
|
||||||
<Logger name="app.msgbus" level="info" />
|
<Logger name="app.msgbus" level="info" />
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
|
[app.common.types.grid :as ctg]
|
||||||
[app.common.types.page :as ctp]
|
[app.common.types.page :as ctp]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.pages-list :as ctpl]
|
||||||
[app.common.types.shape :as cts]
|
[app.common.types.shape :as cts]
|
||||||
|
@ -105,10 +106,20 @@
|
||||||
;; FILE PREPARATION BEFORE MIGRATION
|
;; FILE PREPARATION BEFORE MIGRATION
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(def valid-color? (sm/lazy-validator ::ctc/recent-color))
|
(def valid-recent-color?
|
||||||
(def valid-fill? (sm/lazy-validator ::cts/fill))
|
(sm/lazy-validator ::ctc/recent-color))
|
||||||
(def valid-stroke? (sm/lazy-validator ::cts/stroke))
|
|
||||||
(def valid-flow? (sm/lazy-validator ::ctp/flow))
|
(def valid-color?
|
||||||
|
(sm/lazy-validator ::ctc/color))
|
||||||
|
|
||||||
|
(def valid-fill?
|
||||||
|
(sm/lazy-validator ::cts/fill))
|
||||||
|
|
||||||
|
(def valid-stroke?
|
||||||
|
(sm/lazy-validator ::cts/stroke))
|
||||||
|
|
||||||
|
(def valid-flow?
|
||||||
|
(sm/lazy-validator ::ctp/flow))
|
||||||
|
|
||||||
(def valid-text-content?
|
(def valid-text-content?
|
||||||
(sm/lazy-validator ::ctsx/content))
|
(sm/lazy-validator ::ctsx/content))
|
||||||
|
@ -122,30 +133,61 @@
|
||||||
(def valid-rgb-color-string?
|
(def valid-rgb-color-string?
|
||||||
(sm/lazy-validator ::ctc/rgb-color))
|
(sm/lazy-validator ::ctc/rgb-color))
|
||||||
|
|
||||||
|
(def valid-shape-points?
|
||||||
|
(sm/lazy-validator ::cts/points))
|
||||||
|
|
||||||
|
(def valid-image-attrs?
|
||||||
|
(sm/lazy-validator ::cts/image-attrs))
|
||||||
|
|
||||||
|
(def valid-column-grid-params?
|
||||||
|
(sm/lazy-validator ::ctg/column-params))
|
||||||
|
|
||||||
|
(def valid-square-grid-params?
|
||||||
|
(sm/lazy-validator ::ctg/square-params))
|
||||||
|
|
||||||
|
|
||||||
(defn- prepare-file-data
|
(defn- prepare-file-data
|
||||||
"Apply some specific migrations or fixes to things that are allowed in v1 but not in v2,
|
"Apply some specific migrations or fixes to things that are allowed in v1 but not in v2,
|
||||||
or that are the result of old bugs."
|
or that are the result of old bugs."
|
||||||
[file-data libraries]
|
[file-data libraries]
|
||||||
(let [detached-ids (volatile! #{})
|
(let [detached-ids (volatile! #{})
|
||||||
|
|
||||||
detach-shape
|
detach-shape
|
||||||
(fn [container shape]
|
(fn [container shape]
|
||||||
;; Detach a shape. If it's inside a component, add it to detached-ids. This list
|
;; Detach a shape and make necessary adjustments.
|
||||||
;; is used later to process any other copy that was referencing a detached copy.
|
|
||||||
(let [is-component? (let [root-shape (ctst/get-shape container (:id container))]
|
(let [is-component? (let [root-shape (ctst/get-shape container (:id container))]
|
||||||
(and (some? root-shape) (nil? (:parent-id root-shape))))]
|
(and (some? root-shape) (nil? (:parent-id root-shape))))
|
||||||
|
parent (ctst/get-shape container (:parent-id shape))
|
||||||
|
in-copy? (ctn/in-any-component? (:objects container) parent)]
|
||||||
|
|
||||||
|
(letfn [(detach-recursive [container shape first?]
|
||||||
|
|
||||||
|
;; If the shape is inside a component, add it to detached-ids. This list is used
|
||||||
|
;; later to process other copies that was referencing a detached nested copy.
|
||||||
(when is-component?
|
(when is-component?
|
||||||
(vswap! detached-ids conj (:id shape)))
|
(vswap! detached-ids conj (:id shape)))
|
||||||
(ctk/detach-shape shape)))
|
|
||||||
|
;; Detach the shape and all children until we find a subinstance.
|
||||||
|
(if (or first? in-copy? (not (ctk/instance-head? shape)))
|
||||||
|
(as-> container $
|
||||||
|
(ctn/update-shape $ (:id shape) ctk/detach-shape)
|
||||||
|
(reduce #(detach-recursive %1 %2 false)
|
||||||
|
$
|
||||||
|
(map (d/getf (:objects container)) (:shapes shape))))
|
||||||
|
|
||||||
|
;; If this is a subinstance head and the initial shape whas not itself a
|
||||||
|
;; nested copy, stop detaching and promote it to root.
|
||||||
|
(ctn/update-shape container (:id shape) #(assoc % :component-root true))))]
|
||||||
|
|
||||||
|
(detach-recursive container shape true))))
|
||||||
|
|
||||||
fix-bad-children
|
fix-bad-children
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Remove any child that does not exist. And also remove duplicated children.
|
;; Remove any child that does not exist. And also remove duplicated children.
|
||||||
(letfn [(fix-container
|
(letfn [(fix-container [container]
|
||||||
[container]
|
|
||||||
(d/update-when container :objects update-vals (partial fix-shape container)))
|
(d/update-when container :objects update-vals (partial fix-shape container)))
|
||||||
|
|
||||||
(fix-shape
|
(fix-shape [container shape]
|
||||||
[container shape]
|
|
||||||
(let [objects (:objects container)]
|
(let [objects (:objects container)]
|
||||||
(d/update-when shape :shapes
|
(d/update-when shape :shapes
|
||||||
(fn [shapes]
|
(fn [shapes]
|
||||||
|
@ -160,12 +202,10 @@
|
||||||
fix-missing-image-metadata
|
fix-missing-image-metadata
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Delete broken image shapes with no metadata.
|
;; Delete broken image shapes with no metadata.
|
||||||
(letfn [(fix-container
|
(letfn [(fix-container [container]
|
||||||
[container]
|
|
||||||
(d/update-when container :objects #(reduce-kv fix-shape % %)))
|
(d/update-when container :objects #(reduce-kv fix-shape % %)))
|
||||||
|
|
||||||
(fix-shape
|
(fix-shape [objects id shape]
|
||||||
[objects id shape]
|
|
||||||
(if (and (cfh/image-shape? shape)
|
(if (and (cfh/image-shape? shape)
|
||||||
(nil? (:metadata shape)))
|
(nil? (:metadata shape)))
|
||||||
(-> objects
|
(-> objects
|
||||||
|
@ -189,11 +229,28 @@
|
||||||
(dissoc options :background)
|
(dissoc options :background)
|
||||||
options))
|
options))
|
||||||
|
|
||||||
|
(fix-saved-grids [options]
|
||||||
|
(d/update-when options :saved-grids
|
||||||
|
(fn [grids]
|
||||||
|
(cond-> grids
|
||||||
|
(and (contains? grids :column)
|
||||||
|
(not (valid-column-grid-params? (:column grids))))
|
||||||
|
(dissoc :column)
|
||||||
|
|
||||||
|
(and (contains? grids :row)
|
||||||
|
(not (valid-column-grid-params? (:row grids))))
|
||||||
|
(dissoc :row)
|
||||||
|
|
||||||
|
(and (contains? grids :square)
|
||||||
|
(not (valid-square-grid-params? (:square grids))))
|
||||||
|
(dissoc :square)))))
|
||||||
|
|
||||||
(fix-options [options]
|
(fix-options [options]
|
||||||
(-> options
|
(-> options
|
||||||
;; Some pages has invalid data on flows, we proceed just to
|
;; Some pages has invalid data on flows, we proceed just to
|
||||||
;; delete them.
|
;; delete them.
|
||||||
(d/update-when :flows #(filterv valid-flow? %))
|
(d/update-when :flows #(filterv valid-flow? %))
|
||||||
|
(fix-saved-grids)
|
||||||
(fix-background)))]
|
(fix-background)))]
|
||||||
|
|
||||||
(update file-data :pages-index update-vals update-page)))
|
(update file-data :pages-index update-vals update-page)))
|
||||||
|
@ -203,11 +260,19 @@
|
||||||
;; fix that issues.
|
;; fix that issues.
|
||||||
fix-file-data
|
fix-file-data
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
|
(letfn [(fix-colors-library [colors]
|
||||||
|
(let [colors (dissoc colors nil)]
|
||||||
|
(reduce-kv (fn [colors id color]
|
||||||
|
(if (valid-color? color)
|
||||||
|
colors
|
||||||
|
(dissoc colors id)))
|
||||||
|
colors
|
||||||
|
colors)))]
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(d/update-when :colors dissoc nil)
|
(d/update-when :colors fix-colors-library)
|
||||||
(d/update-when :typographies dissoc nil)))
|
(d/update-when :typographies dissoc nil))))
|
||||||
|
|
||||||
delete-big-geometry-shapes
|
fix-big-geometry-shapes
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; At some point in time, we had a bug that generated shapes
|
;; At some point in time, we had a bug that generated shapes
|
||||||
;; with huge geometries that did not validate the
|
;; with huge geometries that did not validate the
|
||||||
|
@ -253,9 +318,16 @@
|
||||||
(fn [shapes] (filterv #(not= id %) shapes)))))
|
(fn [shapes] (filterv #(not= id %) shapes)))))
|
||||||
|
|
||||||
(and (cfh/text-shape? shape)
|
(and (cfh/text-shape? shape)
|
||||||
(not (seq (:content shape))))
|
(not (valid-text-content? (:content shape))))
|
||||||
(dissoc objects id)
|
(dissoc objects id)
|
||||||
|
|
||||||
|
(and (cfh/path-shape? shape)
|
||||||
|
(not (valid-path-content? (:content shape))))
|
||||||
|
(-> objects
|
||||||
|
(dissoc id)
|
||||||
|
(d/update-in-when [(:parent-id shape) :shapes]
|
||||||
|
(fn [shapes] (filterv #(not= id %) shapes))))
|
||||||
|
|
||||||
:else
|
:else
|
||||||
objects))
|
objects))
|
||||||
|
|
||||||
|
@ -266,25 +338,125 @@
|
||||||
(update :pages-index update-vals update-container)
|
(update :pages-index update-vals update-container)
|
||||||
(update :components update-vals update-container))))
|
(update :components update-vals update-container))))
|
||||||
|
|
||||||
fix-misc-shape-issues
|
fix-shape-geometry
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
(letfn [(fix-container [container]
|
(letfn [(fix-container [container]
|
||||||
(d/update-when container :objects update-vals fix-shape))
|
(d/update-when container :objects update-vals fix-shape))
|
||||||
|
|
||||||
(fix-shape [shape]
|
(fix-shape [shape]
|
||||||
(cond-> shape
|
(cond
|
||||||
;; Some shapes has invalid gap value
|
(and (cfh/image-shape? shape)
|
||||||
(contains? shape :layout-gap)
|
(valid-image-attrs? shape)
|
||||||
(d/update-in-when [:layout-gap :column-gap]
|
(grc/valid-rect? (:selrect shape))
|
||||||
(fn [gap]
|
(not (valid-shape-points? (:points shape))))
|
||||||
|
(let [selrect (:selrect shape)
|
||||||
|
metadata (:metadata shape)
|
||||||
|
selrect (grc/make-rect
|
||||||
|
(:x selrect)
|
||||||
|
(:y selrect)
|
||||||
|
(:width metadata)
|
||||||
|
(:height metadata))
|
||||||
|
points (grc/rect->points selrect)]
|
||||||
|
(assoc shape
|
||||||
|
:selrect selrect
|
||||||
|
:points points))
|
||||||
|
|
||||||
|
(and (cfh/text-shape? shape)
|
||||||
|
(valid-text-content? (:content shape))
|
||||||
|
(not (valid-shape-points? (:points shape)))
|
||||||
|
(seq (:position-data shape)))
|
||||||
|
(let [selrect (->> (:position-data shape)
|
||||||
|
(map (juxt :x :y :width :height))
|
||||||
|
(map #(apply grc/make-rect %))
|
||||||
|
(grc/join-rects))
|
||||||
|
points (grc/rect->points selrect)]
|
||||||
|
|
||||||
|
(assoc shape
|
||||||
|
:x (:x selrect)
|
||||||
|
:y (:y selrect)
|
||||||
|
:width (:width selrect)
|
||||||
|
:height (:height selrect)
|
||||||
|
:selrect selrect
|
||||||
|
:points points))
|
||||||
|
|
||||||
|
(and (or (cfh/rect-shape? shape)
|
||||||
|
(cfh/svg-raw-shape? shape)
|
||||||
|
(cfh/circle-shape? shape))
|
||||||
|
(not (valid-shape-points? (:points shape)))
|
||||||
|
(grc/valid-rect? (:selrect shape)))
|
||||||
|
(let [selrect (if (grc/valid-rect? (:svg-viewbox shape))
|
||||||
|
(:svg-viewbox shape)
|
||||||
|
(:selrect shape))
|
||||||
|
points (grc/rect->points selrect)]
|
||||||
|
(assoc shape
|
||||||
|
:x (:x selrect)
|
||||||
|
:y (:y selrect)
|
||||||
|
:width (:width selrect)
|
||||||
|
:height (:height selrect)
|
||||||
|
:selrect selrect
|
||||||
|
:points points))
|
||||||
|
|
||||||
|
(and (= :icon (:type shape))
|
||||||
|
(grc/valid-rect? (:selrect shape))
|
||||||
|
(valid-shape-points? (:points shape)))
|
||||||
|
(-> shape
|
||||||
|
(assoc :type :rect)
|
||||||
|
(dissoc :content)
|
||||||
|
(dissoc :metadata)
|
||||||
|
(dissoc :segments)
|
||||||
|
(dissoc :x1 :y1 :x2 :y2))
|
||||||
|
|
||||||
|
(and (cfh/group-shape? shape)
|
||||||
|
(grc/valid-rect? (:selrect shape))
|
||||||
|
(not (valid-shape-points? (:points shape))))
|
||||||
|
(assoc shape :points (grc/rect->points (:selrect shape)))
|
||||||
|
|
||||||
|
:else
|
||||||
|
shape))]
|
||||||
|
|
||||||
|
(-> file-data
|
||||||
|
(update :pages-index update-vals fix-container)
|
||||||
|
(d/update-when :components update-vals fix-container))))
|
||||||
|
|
||||||
|
fix-misc-shape-issues
|
||||||
|
(fn [file-data]
|
||||||
|
(letfn [(fix-container [container]
|
||||||
|
(d/update-when container :objects update-vals fix-shape))
|
||||||
|
|
||||||
|
(fix-gap-value [gap]
|
||||||
(if (or (= gap ##Inf)
|
(if (or (= gap ##Inf)
|
||||||
(= gap ##-Inf))
|
(= gap ##-Inf))
|
||||||
0
|
0
|
||||||
gap)))
|
gap))
|
||||||
|
|
||||||
|
(fix-shape [shape]
|
||||||
|
(cond-> shape
|
||||||
|
;; Some shapes has invalid gap value
|
||||||
|
(contains? shape :layout-gap)
|
||||||
|
(update :layout-gap (fn [layout-gap]
|
||||||
|
(if (number? layout-gap)
|
||||||
|
{:row-gap layout-gap :column-gap layout-gap}
|
||||||
|
(-> layout-gap
|
||||||
|
(d/update-when :column-gap fix-gap-value)
|
||||||
|
(d/update-when :row-gap fix-gap-value)))))
|
||||||
|
|
||||||
|
;; Fix name if missing
|
||||||
(nil? (:name shape))
|
(nil? (:name shape))
|
||||||
(assoc :name (d/name (:type shape)))
|
(assoc :name (d/name (:type shape)))
|
||||||
|
|
||||||
|
;; Remove v2 info from components that have been copied and pasted
|
||||||
|
;; from a v2 file
|
||||||
|
(some? (:main-instance shape))
|
||||||
|
(dissoc :main-instance)
|
||||||
|
|
||||||
|
(and (contains? shape :transform)
|
||||||
|
(not (gmt/valid-matrix? (:transform shape))))
|
||||||
|
(assoc :transform (gmt/matrix))
|
||||||
|
|
||||||
|
(and (contains? shape :transform-inverse)
|
||||||
|
(not (gmt/valid-matrix? (:transform-inverse shape))))
|
||||||
|
(assoc :transform-inverse (gmt/matrix))
|
||||||
|
|
||||||
;; Fix broken fills
|
;; Fix broken fills
|
||||||
(seq (:fills shape))
|
(seq (:fills shape))
|
||||||
(update :fills (fn [fills] (filterv valid-fill? fills)))
|
(update :fills (fn [fills] (filterv valid-fill? fills)))
|
||||||
|
@ -296,11 +468,7 @@
|
||||||
;; Fix some broken layout related attrs, probably
|
;; Fix some broken layout related attrs, probably
|
||||||
;; of copypaste on flex layout betatest period
|
;; of copypaste on flex layout betatest period
|
||||||
(true? (:layout shape))
|
(true? (:layout shape))
|
||||||
(assoc :layout :flex)
|
(assoc :layout :flex)))]
|
||||||
|
|
||||||
(number? (:layout-gap shape))
|
|
||||||
(as-> shape (let [n (:layout-gap shape)]
|
|
||||||
(assoc shape :layout-gap {:row-gap n :column-gap n})))))]
|
|
||||||
|
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(update :pages-index update-vals fix-container)
|
(update :pages-index update-vals fix-container)
|
||||||
|
@ -342,13 +510,15 @@
|
||||||
(and (cfh/path-shape? shape)
|
(and (cfh/path-shape? shape)
|
||||||
(seq (:content shape))
|
(seq (:content shape))
|
||||||
(not (valid-path-content? (:content shape))))
|
(not (valid-path-content? (:content shape))))
|
||||||
(let [shape (update shape :content fix-path-content)
|
(let [shape (update shape :content fix-path-content)]
|
||||||
[points selrect] (gshp/content->points+selrect shape (:content shape))]
|
(if (not (valid-path-content? (:content shape)))
|
||||||
|
shape
|
||||||
|
(let [[points selrect] (gshp/content->points+selrect shape (:content shape))]
|
||||||
(-> shape
|
(-> shape
|
||||||
(dissoc :bool-content)
|
(dissoc :bool-content)
|
||||||
(dissoc :bool-type)
|
(dissoc :bool-type)
|
||||||
(assoc :points points)
|
(assoc :points points)
|
||||||
(assoc :selrect selrect)))
|
(assoc :selrect selrect)))))
|
||||||
|
|
||||||
;; When we fount a bool shape with no content,
|
;; When we fount a bool shape with no content,
|
||||||
;; we convert it to a simple rect
|
;; we convert it to a simple rect
|
||||||
|
@ -390,18 +560,16 @@
|
||||||
;; Remove invalid colors in :recent-colors
|
;; Remove invalid colors in :recent-colors
|
||||||
(d/update-when file-data :recent-colors
|
(d/update-when file-data :recent-colors
|
||||||
(fn [colors]
|
(fn [colors]
|
||||||
(filterv valid-color? colors))))
|
(filterv valid-recent-color? colors))))
|
||||||
|
|
||||||
fix-broken-parents
|
fix-broken-parents
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Find children shapes whose parent-id is not set to the parent that contains them.
|
;; Find children shapes whose parent-id is not set to the parent that contains them.
|
||||||
;; Remove them from the parent :shapes list.
|
;; Remove them from the parent :shapes list.
|
||||||
(letfn [(fix-container
|
(letfn [(fix-container [container]
|
||||||
[container]
|
|
||||||
(d/update-when container :objects #(reduce-kv fix-shape % %)))
|
(d/update-when container :objects #(reduce-kv fix-shape % %)))
|
||||||
|
|
||||||
(fix-shape
|
(fix-shape [objects id shape]
|
||||||
[objects id shape]
|
|
||||||
(reduce (fn [objects child-id]
|
(reduce (fn [objects child-id]
|
||||||
(let [child (get objects child-id)]
|
(let [child (get objects child-id)]
|
||||||
(cond-> objects
|
(cond-> objects
|
||||||
|
@ -476,20 +644,33 @@
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Detach shapes that were inside a copy (have :shape-ref) but now they aren't.
|
;; Detach shapes that were inside a copy (have :shape-ref) but now they aren't.
|
||||||
(letfn [(fix-container [container]
|
(letfn [(fix-container [container]
|
||||||
(d/update-when container :objects update-vals (partial fix-shape container)))
|
(reduce fix-shape container (ctn/shapes-seq container)))
|
||||||
|
|
||||||
(fix-shape [container shape]
|
(fix-shape [container shape]
|
||||||
(let [parent (ctst/get-shape container (:parent-id shape))]
|
(let [shape (ctst/get-shape container (:id shape)) ; Get the possibly updated shape
|
||||||
|
parent (ctst/get-shape container (:parent-id shape))]
|
||||||
(if (and (ctk/in-component-copy? shape)
|
(if (and (ctk/in-component-copy? shape)
|
||||||
(not (ctk/instance-head? shape))
|
(not (ctk/instance-head? shape))
|
||||||
(not (ctk/in-component-copy? parent)))
|
(not (ctk/in-component-copy? parent)))
|
||||||
(detach-shape container shape)
|
(detach-shape container shape)
|
||||||
shape)))]
|
container)))]
|
||||||
|
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(update :pages-index update-vals fix-container)
|
(update :pages-index update-vals fix-container)
|
||||||
(d/update-when :components update-vals fix-container))))
|
(d/update-when :components update-vals fix-container))))
|
||||||
|
|
||||||
|
fix-components-without-id
|
||||||
|
(fn [file-data]
|
||||||
|
;; We have detected some components that have no :id attribute.
|
||||||
|
;; Regenerate it from the components map.
|
||||||
|
(letfn [(fix-component [id component]
|
||||||
|
(if (some? (:id component))
|
||||||
|
component
|
||||||
|
(assoc component :id id)))]
|
||||||
|
|
||||||
|
(-> file-data
|
||||||
|
(d/update-when :components #(d/mapm fix-component %)))))
|
||||||
|
|
||||||
remap-refs
|
remap-refs
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Remap shape-refs so that they point to the near main.
|
;; Remap shape-refs so that they point to the near main.
|
||||||
|
@ -523,11 +704,9 @@
|
||||||
(if (some? direct-shape-2)
|
(if (some? direct-shape-2)
|
||||||
;; If it exists, there is nothing else to do.
|
;; If it exists, there is nothing else to do.
|
||||||
container
|
container
|
||||||
;; If not found, detach shape and all children (stopping if a nested instance is reached)
|
;; If not found, detach shape and all children.
|
||||||
(let [children (ctn/get-children-in-instance (:objects container) (:id shape))]
|
;; container
|
||||||
(reduce #(ctn/update-shape %1 (:id %2) (partial detach-shape %1))
|
(detach-shape container shape)))))))
|
||||||
container
|
|
||||||
children))))))))
|
|
||||||
container))]
|
container))]
|
||||||
|
|
||||||
(-> file-data
|
(-> file-data
|
||||||
|
@ -539,14 +718,64 @@
|
||||||
;; If the user has created a copy and then converted into a path or bool,
|
;; If the user has created a copy and then converted into a path or bool,
|
||||||
;; detach it because the synchronization will no longer work.
|
;; detach it because the synchronization will no longer work.
|
||||||
(letfn [(fix-container [container]
|
(letfn [(fix-container [container]
|
||||||
(d/update-when container :objects update-vals (partial fix-shape container)))
|
(reduce fix-shape container (ctn/shapes-seq container)))
|
||||||
|
|
||||||
(fix-shape [container shape]
|
(fix-shape [container shape]
|
||||||
(if (and (ctk/instance-head? shape)
|
(if (and (ctk/instance-head? shape)
|
||||||
(or (cfh/path-shape? shape)
|
(or (cfh/path-shape? shape)
|
||||||
(cfh/bool-shape? shape)))
|
(cfh/bool-shape? shape)))
|
||||||
(detach-shape container shape)
|
(detach-shape container shape)
|
||||||
shape))]
|
container))]
|
||||||
|
|
||||||
|
(-> file-data
|
||||||
|
(update :pages-index update-vals fix-container)
|
||||||
|
(d/update-when :components update-vals fix-container))))
|
||||||
|
|
||||||
|
wrap-non-group-component-roots
|
||||||
|
(fn [file-data]
|
||||||
|
;; Some components have a root that is not a group nor a frame
|
||||||
|
;; (e.g. a path or a svg-raw). We need to wrap them in a frame
|
||||||
|
;; for this one to became the root.
|
||||||
|
(letfn [(fix-component [component]
|
||||||
|
(let [root-shape (ctst/get-shape component (:id component))]
|
||||||
|
(if (or (cfh/group-shape? root-shape)
|
||||||
|
(cfh/frame-shape? root-shape))
|
||||||
|
component
|
||||||
|
(let [new-id (uuid/next)
|
||||||
|
frame (-> (cts/setup-shape
|
||||||
|
{:type :frame
|
||||||
|
:id (:id component)
|
||||||
|
:x (:x (:selrect root-shape))
|
||||||
|
:y (:y (:selrect root-shape))
|
||||||
|
:width (:width (:selrect root-shape))
|
||||||
|
:height (:height (:selrect root-shape))
|
||||||
|
:name (:name component)
|
||||||
|
:shapes [new-id]})
|
||||||
|
(assoc :frame-id nil
|
||||||
|
:parent-id nil))
|
||||||
|
root-shape' (assoc root-shape
|
||||||
|
:id new-id
|
||||||
|
:parent-id (:id frame)
|
||||||
|
:frame-id (:id frame))]
|
||||||
|
(update component :objects assoc
|
||||||
|
(:id frame) frame
|
||||||
|
(:id root-shape') root-shape')))))]
|
||||||
|
|
||||||
|
(-> file-data
|
||||||
|
(d/update-when :components update-vals fix-component))))
|
||||||
|
|
||||||
|
detach-non-group-instance-roots
|
||||||
|
(fn [file-data]
|
||||||
|
;; If there is a copy instance whose root is not a frame or a group, it cannot
|
||||||
|
;; be easily repaired, and anyway it's not working in production, so detach it.
|
||||||
|
(letfn [(fix-container [container]
|
||||||
|
(reduce fix-shape container (ctn/shapes-seq container)))
|
||||||
|
|
||||||
|
(fix-shape [container shape]
|
||||||
|
(if (and (ctk/instance-head? shape)
|
||||||
|
(not (#{:group :frame} (:type shape))))
|
||||||
|
(detach-shape container shape)
|
||||||
|
container))]
|
||||||
|
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(update :pages-index update-vals fix-container)
|
(update :pages-index update-vals fix-container)
|
||||||
|
@ -554,7 +783,7 @@
|
||||||
|
|
||||||
transform-to-frames
|
transform-to-frames
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Transform component and copy heads to frames, and set the
|
;; Transform component and copy heads fron group to frames, and set the
|
||||||
;; frame-id of its childrens
|
;; frame-id of its childrens
|
||||||
(letfn [(fix-container [container]
|
(letfn [(fix-container [container]
|
||||||
(d/update-when container :objects update-vals fix-shape))
|
(d/update-when container :objects update-vals fix-shape))
|
||||||
|
@ -631,9 +860,8 @@
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Find component heads that are not main-instance but have not :shape-ref.
|
;; Find component heads that are not main-instance but have not :shape-ref.
|
||||||
;; Also shapes that have :shape-ref but are not in a copy.
|
;; Also shapes that have :shape-ref but are not in a copy.
|
||||||
(letfn [(fix-container
|
(letfn [(fix-container [container]
|
||||||
[container]
|
(reduce fix-shape container (ctn/shapes-seq container)))
|
||||||
(d/update-when container :objects update-vals (partial fix-shape container)))
|
|
||||||
|
|
||||||
(fix-shape
|
(fix-shape
|
||||||
[container shape]
|
[container shape]
|
||||||
|
@ -643,11 +871,30 @@
|
||||||
(and (ctk/in-component-copy? shape)
|
(and (ctk/in-component-copy? shape)
|
||||||
(nil? (ctn/get-head-shape (:objects container) shape {:allow-main? true}))))
|
(nil? (ctn/get-head-shape (:objects container) shape {:allow-main? true}))))
|
||||||
(detach-shape container shape)
|
(detach-shape container shape)
|
||||||
shape))]
|
container))]
|
||||||
|
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(update :pages-index update-vals fix-container)
|
(update :pages-index update-vals fix-container)
|
||||||
(d/update-when :components update-vals fix-container))))
|
(d/update-when :components update-vals fix-container))))
|
||||||
|
|
||||||
|
|
||||||
|
fix-component-root-without-component
|
||||||
|
(fn [file-data]
|
||||||
|
;; Ensure that if component-root is set component-file and component-id are set too
|
||||||
|
(letfn [(fix-container [container]
|
||||||
|
(d/update-when container :objects update-vals fix-shape))
|
||||||
|
|
||||||
|
(fix-shape [shape]
|
||||||
|
(cond-> shape
|
||||||
|
(and (ctk/instance-root? shape)
|
||||||
|
(or (not (ctk/instance-head? shape))
|
||||||
|
(not (some? (:component-file shape)))))
|
||||||
|
(dissoc :component-id
|
||||||
|
:component-file
|
||||||
|
:component-root)))]
|
||||||
|
(-> file-data
|
||||||
|
(update :pages-index update-vals fix-container))))
|
||||||
|
|
||||||
fix-copies-of-detached
|
fix-copies-of-detached
|
||||||
(fn [file-data]
|
(fn [file-data]
|
||||||
;; Find any copy that is referencing a shape inside a component that have
|
;; Find any copy that is referencing a shape inside a component that have
|
||||||
|
@ -661,56 +908,42 @@
|
||||||
(fix-shape [shape]
|
(fix-shape [shape]
|
||||||
(cond-> shape
|
(cond-> shape
|
||||||
(@detached-ids (:shape-ref shape))
|
(@detached-ids (:shape-ref shape))
|
||||||
(dissoc shape
|
(ctk/detach-shape)))]
|
||||||
:component-id
|
|
||||||
:component-file
|
|
||||||
:component-root)))]
|
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(update :pages-index update-vals fix-container)
|
(update :pages-index update-vals fix-container)
|
||||||
(d/update-when :components update-vals fix-container))))
|
(d/update-when :components update-vals fix-container))))]
|
||||||
|
|
||||||
fix-shape-nil-parent-id
|
|
||||||
(fn [file-data]
|
|
||||||
;; Ensure that parent-id and frame-id are not nil
|
|
||||||
(letfn [(fix-container [container]
|
|
||||||
(d/update-when container :objects update-vals fix-shape))
|
|
||||||
|
|
||||||
(fix-shape [shape]
|
|
||||||
(let [frame-id (or (:frame-id shape)
|
|
||||||
uuid/zero)
|
|
||||||
parent-id (or (:parent-id shape)
|
|
||||||
frame-id)]
|
|
||||||
(assoc shape :frame-id frame-id
|
|
||||||
:parent-id parent-id)))]
|
|
||||||
(-> file-data
|
|
||||||
(update :pages-index update-vals fix-container))))]
|
|
||||||
|
|
||||||
(-> file-data
|
(-> file-data
|
||||||
(fix-file-data)
|
(fix-file-data)
|
||||||
(fix-page-invalid-options)
|
(fix-page-invalid-options)
|
||||||
(fix-completly-broken-shapes)
|
|
||||||
(fix-bad-children)
|
|
||||||
(fix-misc-shape-issues)
|
(fix-misc-shape-issues)
|
||||||
(fix-recent-colors)
|
(fix-recent-colors)
|
||||||
(fix-missing-image-metadata)
|
(fix-missing-image-metadata)
|
||||||
(fix-text-shapes-converted-to-path)
|
(fix-text-shapes-converted-to-path)
|
||||||
(fix-broken-paths)
|
(fix-broken-paths)
|
||||||
(delete-big-geometry-shapes)
|
(fix-big-geometry-shapes)
|
||||||
|
(fix-shape-geometry)
|
||||||
|
(fix-completly-broken-shapes)
|
||||||
|
(fix-bad-children)
|
||||||
(fix-broken-parents)
|
(fix-broken-parents)
|
||||||
(fix-orphan-shapes)
|
(fix-orphan-shapes)
|
||||||
(fix-orphan-copies)
|
(fix-orphan-copies)
|
||||||
(remove-nested-roots)
|
(remove-nested-roots)
|
||||||
(add-not-nested-roots)
|
(add-not-nested-roots)
|
||||||
|
(fix-components-without-id)
|
||||||
(remap-refs)
|
(remap-refs)
|
||||||
(fix-converted-copies)
|
(fix-converted-copies)
|
||||||
|
(wrap-non-group-component-roots)
|
||||||
|
(detach-non-group-instance-roots)
|
||||||
(transform-to-frames)
|
(transform-to-frames)
|
||||||
(remap-frame-ids)
|
(remap-frame-ids)
|
||||||
(fix-frame-ids)
|
(fix-frame-ids)
|
||||||
(fix-component-nil-objects)
|
(fix-component-nil-objects)
|
||||||
(fix-false-copies)
|
(fix-false-copies)
|
||||||
(fix-shape-nil-parent-id)
|
(fix-component-root-without-component)
|
||||||
(fix-copies-of-detached)))) ; <- Do not add fixes after this one
|
(fix-copies-of-detached); <- Do not add fixes after this and fix-orphan-copies call
|
||||||
|
; This extra call to fix-orphan-copies after fix-copies-of-detached because we can have detached subtrees with invalid shape-ref attributes
|
||||||
|
(fix-orphan-copies))))
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; COMPONENTS MIGRATION
|
;; COMPONENTS MIGRATION
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
|
@ -100,8 +100,8 @@
|
||||||
(let [profile (profile/get-profile pool profile-id)
|
(let [profile (profile/get-profile pool profile-id)
|
||||||
project-id (:default-project-id profile)]
|
project-id (:default-project-id profile)]
|
||||||
|
|
||||||
(db/run! pool (fn [{:keys [::db/conn]}]
|
(db/run! pool (fn [{:keys [::db/conn] :as cfg}]
|
||||||
(create-file conn {:id file-id
|
(create-file cfg {:id file-id
|
||||||
:name (str "Cloned file: " filename)
|
:name (str "Cloned file: " filename)
|
||||||
:project-id project-id
|
:project-id project-id
|
||||||
:profile-id profile-id})
|
:profile-id profile-id})
|
||||||
|
@ -141,8 +141,8 @@
|
||||||
{::rres/status 200
|
{::rres/status 200
|
||||||
::rres/body "OK UPDATED"})
|
::rres/body "OK UPDATED"})
|
||||||
|
|
||||||
(db/run! pool (fn [{:keys [::db/conn]}]
|
(db/run! pool (fn [{:keys [::db/conn] :as cfg}]
|
||||||
(create-file conn {:id file-id
|
(create-file cfg {:id file-id
|
||||||
:name fname
|
:name fname
|
||||||
:project-id project-id
|
:project-id project-id
|
||||||
:profile-id profile-id})
|
:profile-id profile-id})
|
||||||
|
|
|
@ -131,8 +131,8 @@
|
||||||
(defn- invoke
|
(defn- invoke
|
||||||
[limiter metrics limit-id limit-key limit-label profile-id f params]
|
[limiter metrics limit-id limit-key limit-label profile-id f params]
|
||||||
(let [tpoint (dt/tpoint)
|
(let [tpoint (dt/tpoint)
|
||||||
|
mlabels (into-array String [(id->str limit-id)])
|
||||||
limit-id (id->str limit-id limit-key)
|
limit-id (id->str limit-id limit-key)
|
||||||
mlabels (into-array String [limit-id])
|
|
||||||
stats (pbh/get-stats limiter)
|
stats (pbh/get-stats limiter)
|
||||||
id (.incrementAndGet ^AtomicLong idseq)]
|
id (.incrementAndGet ^AtomicLong idseq)]
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
(:refer-clojure :exclude [assert])
|
(:refer-clojure :exclude [assert])
|
||||||
(:require
|
(:require
|
||||||
[app.binfile.v1 :as bf.v1]
|
[app.binfile.v1 :as bf.v1]
|
||||||
|
[app.common.logging :as l]
|
||||||
[app.common.schema :as sm]
|
[app.common.schema :as sm]
|
||||||
[app.db :as db]
|
[app.db :as db]
|
||||||
[app.http.sse :as sse]
|
[app.http.sse :as sse]
|
||||||
|
@ -50,11 +51,16 @@
|
||||||
::rres/headers {"content-type" "application/octet-stream"}
|
::rres/headers {"content-type" "application/octet-stream"}
|
||||||
::rres/body (reify rres/StreamableResponseBody
|
::rres/body (reify rres/StreamableResponseBody
|
||||||
(-write-body-to-stream [_ _ output-stream]
|
(-write-body-to-stream [_ _ output-stream]
|
||||||
|
(try
|
||||||
(-> cfg
|
(-> cfg
|
||||||
(assoc ::bf.v1/ids #{file-id})
|
(assoc ::bf.v1/ids #{file-id})
|
||||||
(assoc ::bf.v1/embed-assets embed-assets)
|
(assoc ::bf.v1/embed-assets embed-assets)
|
||||||
(assoc ::bf.v1/include-libraries include-libraries)
|
(assoc ::bf.v1/include-libraries include-libraries)
|
||||||
(bf.v1/export-files! output-stream))))}))
|
(bf.v1/export-files! output-stream))
|
||||||
|
(catch Throwable cause
|
||||||
|
(l/err :hint "exception on exporting file"
|
||||||
|
:file-id (str file-id)
|
||||||
|
:cause cause)))))}))
|
||||||
|
|
||||||
;; --- Command: import-binfile
|
;; --- Command: import-binfile
|
||||||
|
|
||||||
|
|
|
@ -188,17 +188,27 @@
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
(defn migrate-file!
|
(defn migrate-file!
|
||||||
[file-id & {:keys [rollback? validate? label] :or {rollback? true validate? false}}]
|
[file-id & {:keys [rollback? validate? label cache skip-on-graphic-error?]
|
||||||
|
:or {rollback? true
|
||||||
|
validate? false
|
||||||
|
skip-on-graphic-error? true}}]
|
||||||
(l/dbg :hint "migrate:start" :rollback rollback?)
|
(l/dbg :hint "migrate:start" :rollback rollback?)
|
||||||
(let [tpoint (dt/tpoint)
|
(let [tpoint (dt/tpoint)
|
||||||
file-id (if (string? file-id)
|
file-id (if (string? file-id)
|
||||||
(parse-uuid file-id)
|
(parse-uuid file-id)
|
||||||
file-id)]
|
file-id)
|
||||||
(binding [feat/*stats* (atom {})]
|
cache (if (int? cache)
|
||||||
|
(cache/create :executor (::wrk/executor main/system)
|
||||||
|
:max-items cache)
|
||||||
|
nil)]
|
||||||
|
|
||||||
|
(binding [feat/*stats* (atom {})
|
||||||
|
feat/*cache* cache]
|
||||||
(try
|
(try
|
||||||
(-> (assoc main/system ::db/rollback rollback?)
|
(-> (assoc main/system ::db/rollback rollback?)
|
||||||
(feat/migrate-file! file-id
|
(feat/migrate-file! file-id
|
||||||
:validate? validate?
|
:validate? validate?
|
||||||
|
:skip-on-graphic-error? skip-on-graphic-error?
|
||||||
:label label))
|
:label label))
|
||||||
|
|
||||||
(-> (deref feat/*stats*)
|
(-> (deref feat/*stats*)
|
||||||
|
@ -212,10 +222,10 @@
|
||||||
(l/dbg :hint "migrate:end" :rollback rollback? :elapsed elapsed)))))))
|
(l/dbg :hint "migrate:end" :rollback rollback? :elapsed elapsed)))))))
|
||||||
|
|
||||||
(defn migrate-team!
|
(defn migrate-team!
|
||||||
[team-id & {:keys [rollback? skip-on-graphic-error? validate? label]
|
[team-id & {:keys [rollback? skip-on-graphic-error? validate? label cache]
|
||||||
:or {rollback? true
|
:or {rollback? true
|
||||||
validate? true
|
validate? true
|
||||||
skip-on-graphic-error? false}}]
|
skip-on-graphic-error? true}}]
|
||||||
|
|
||||||
(l/dbg :hint "migrate:start" :rollback rollback?)
|
(l/dbg :hint "migrate:start" :rollback rollback?)
|
||||||
|
|
||||||
|
@ -223,11 +233,17 @@
|
||||||
(parse-uuid team-id)
|
(parse-uuid team-id)
|
||||||
team-id)
|
team-id)
|
||||||
stats (atom {})
|
stats (atom {})
|
||||||
tpoint (dt/tpoint)]
|
tpoint (dt/tpoint)
|
||||||
|
|
||||||
|
cache (if (int? cache)
|
||||||
|
(cache/create :executor (::wrk/executor main/system)
|
||||||
|
:max-items cache)
|
||||||
|
nil)]
|
||||||
|
|
||||||
(add-watch stats :progress-report (report-progress-files tpoint))
|
(add-watch stats :progress-report (report-progress-files tpoint))
|
||||||
|
|
||||||
(binding [feat/*stats* stats]
|
(binding [feat/*stats* stats
|
||||||
|
feat/*cache* cache]
|
||||||
(try
|
(try
|
||||||
(-> (assoc main/system ::db/rollback rollback?)
|
(-> (assoc main/system ::db/rollback rollback?)
|
||||||
(feat/migrate-team! team-id
|
(feat/migrate-team! team-id
|
||||||
|
@ -286,7 +302,7 @@
|
||||||
sprocs (ps/create :permits max-procs)
|
sprocs (ps/create :permits max-procs)
|
||||||
|
|
||||||
cache (if (int? cache)
|
cache (if (int? cache)
|
||||||
(cache/create :executor executor
|
(cache/create :executor (::wrk/executor main/system)
|
||||||
:max-items cache)
|
:max-items cache)
|
||||||
nil)
|
nil)
|
||||||
migrate-team
|
migrate-team
|
||||||
|
@ -382,3 +398,17 @@
|
||||||
(l/dbg :hint "migrate:end"
|
(l/dbg :hint "migrate:end"
|
||||||
:rollback rollback?
|
:rollback rollback?
|
||||||
:elapsed elapsed)))))))
|
:elapsed elapsed)))))))
|
||||||
|
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
;; FILE PROCESS HELPERS
|
||||||
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
|
|
||||||
|
(defn delete-broken-files
|
||||||
|
[{:keys [id data] :as file}]
|
||||||
|
(if (-> data :options :components-v2 true?)
|
||||||
|
(do
|
||||||
|
(l/wrn :hint "found old components-v2 format"
|
||||||
|
:file-id (str id)
|
||||||
|
:file-name (:name file))
|
||||||
|
(assoc file :deleted-at (dt/now)))
|
||||||
|
file))
|
||||||
|
|
|
@ -258,8 +258,11 @@
|
||||||
max-jobs
|
max-jobs
|
||||||
start-at
|
start-at
|
||||||
on-file
|
on-file
|
||||||
|
validate?
|
||||||
rollback?]
|
rollback?]
|
||||||
:or {max-jobs 1
|
:or {max-jobs 1
|
||||||
|
max-items Long/MAX_VALUE
|
||||||
|
validate? true
|
||||||
rollback? true}}]
|
rollback? true}}]
|
||||||
|
|
||||||
(l/dbg :hint "process:start"
|
(l/dbg :hint "process:start"
|
||||||
|
@ -273,19 +276,19 @@
|
||||||
sjobs (ps/create :permits max-jobs)
|
sjobs (ps/create :permits max-jobs)
|
||||||
|
|
||||||
process-file
|
process-file
|
||||||
(fn [file-id tpoint]
|
(fn [file-id idx tpoint]
|
||||||
(try
|
(try
|
||||||
(l/trc :hint "process:file:start" :file-id (str file-id))
|
(l/trc :hint "process:file:start" :file-id (str file-id) :index idx)
|
||||||
(db/tx-run! (assoc main/system ::db/rollback rollback?)
|
(db/tx-run! (assoc main/system ::db/rollback rollback?)
|
||||||
(fn [{:keys [::db/conn] :as system}]
|
(fn [{:keys [::db/conn] :as system}]
|
||||||
(let [file' (get-file* system file-id)
|
(let [file' (get-file* system file-id)
|
||||||
file (binding [*system* system]
|
file (binding [*system* system]
|
||||||
(on-file file'))]
|
(on-file file'))]
|
||||||
|
|
||||||
(when (and (some? file)
|
(when (and (some? file) (not (identical? file file')))
|
||||||
(not (identical? file file')))
|
|
||||||
|
|
||||||
(cfv/validate-file-schema! file)
|
(when validate?
|
||||||
|
(cfv/validate-file-schema! file))
|
||||||
|
|
||||||
(let [file (if (contains? (:features file) "fdata/objects-map")
|
(let [file (if (contains? (:features file) "fdata/objects-map")
|
||||||
(feat.fdata/enable-objects-map file)
|
(feat.fdata/enable-objects-map file)
|
||||||
|
@ -300,36 +303,43 @@
|
||||||
|
|
||||||
(db/update! conn :file
|
(db/update! conn :file
|
||||||
{:data (blob/encode (:data file))
|
{:data (blob/encode (:data file))
|
||||||
|
:deleted-at (:deleted-at file)
|
||||||
|
:created-at (:created-at file)
|
||||||
|
:modified-at (:modified-at file)
|
||||||
:features (db/create-array conn "text" (:features file))
|
:features (db/create-array conn "text" (:features file))
|
||||||
:revn (:revn file)}
|
:revn (:revn file)}
|
||||||
{:id file-id}))))))
|
{:id file-id}))))))
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/wrn :hint "unexpected error on processing file (skiping)"
|
(l/wrn :hint "unexpected error on processing file (skiping)"
|
||||||
:file-id (str file-id)
|
:file-id (str file-id)
|
||||||
|
:index idx
|
||||||
:cause cause))
|
:cause cause))
|
||||||
(finally
|
(finally
|
||||||
(ps/release! sjobs)
|
(ps/release! sjobs)
|
||||||
(let [elapsed (dt/format-duration (tpoint))]
|
(let [elapsed (dt/format-duration (tpoint))]
|
||||||
(l/trc :hint "process:file:end"
|
(l/trc :hint "process:file:end"
|
||||||
:file-id (str file-id)
|
:file-id (str file-id)
|
||||||
|
:index idx
|
||||||
:elapsed elapsed)))))]
|
:elapsed elapsed)))))]
|
||||||
|
|
||||||
|
|
||||||
(try
|
(try
|
||||||
(db/tx-run! main/system
|
(db/tx-run! main/system
|
||||||
(fn [{:keys [::db/conn] :as system}]
|
(fn [{:keys [::db/conn] :as system}]
|
||||||
(db/exec! conn ["SET statement_timeout = 0"])
|
(db/exec! conn ["SET statement_timeout = 0"])
|
||||||
(db/exec! conn ["SET idle_in_transaction_session_timeout = 0"])
|
(db/exec! conn ["SET idle_in_transaction_session_timeout = 0"])
|
||||||
|
|
||||||
(run! (fn [file-id]
|
(try
|
||||||
|
(reduce (fn [idx file-id]
|
||||||
(ps/acquire! sjobs)
|
(ps/acquire! sjobs)
|
||||||
(px/run! executor (partial process-file file-id (dt/tpoint))))
|
(px/run! executor (partial process-file file-id idx (dt/tpoint)))
|
||||||
|
(inc idx))
|
||||||
|
0
|
||||||
(->> (db/cursor conn [sql:get-file-ids (or start-at (dt/now))])
|
(->> (db/cursor conn [sql:get-file-ids (or start-at (dt/now))])
|
||||||
(take max-items)
|
(take max-items)
|
||||||
(map :id)))
|
(map :id)))
|
||||||
|
(finally
|
||||||
;; Close and await tasks
|
;; Close and await tasks
|
||||||
(pu/close! executor)))
|
(pu/close! executor)))))
|
||||||
|
|
||||||
(catch Throwable cause
|
(catch Throwable cause
|
||||||
(l/dbg :hint "process:error" :cause cause))
|
(l/dbg :hint "process:error" :cause cause))
|
||||||
|
|
|
@ -57,6 +57,14 @@
|
||||||
#?(:cljs (instance? lkm/LinkedMap o)
|
#?(:cljs (instance? lkm/LinkedMap o)
|
||||||
:clj (instance? LinkedMap o)))
|
:clj (instance? LinkedMap o)))
|
||||||
|
|
||||||
|
(defn vec2
|
||||||
|
"Creates a optimized vector compatible type of length 2 backed
|
||||||
|
internally with MapEntry impl because it has faster access method
|
||||||
|
for its fields."
|
||||||
|
[o1 o2]
|
||||||
|
#?(:clj (clojure.lang.MapEntry. o1 o2)
|
||||||
|
:cljs (cljs.core/->MapEntry o1 o2 nil)))
|
||||||
|
|
||||||
#?(:clj
|
#?(:clj
|
||||||
(defmethod print-method clojure.lang.PersistentQueue [q, w]
|
(defmethod print-method clojure.lang.PersistentQueue [q, w]
|
||||||
;; Overload the printer for queues so they look like fish
|
;; Overload the printer for queues so they look like fish
|
||||||
|
@ -308,9 +316,12 @@
|
||||||
(defn mapm
|
(defn mapm
|
||||||
"Map over the values of a map"
|
"Map over the values of a map"
|
||||||
([mfn]
|
([mfn]
|
||||||
(map (fn [[key val]] [key (mfn key val)])))
|
(map (fn [[key val]] (vec2 key (mfn key val)))))
|
||||||
([mfn coll]
|
([mfn coll]
|
||||||
(into {} (mapm mfn) coll)))
|
(reduce-kv (fn [coll k v]
|
||||||
|
(assoc coll k (mfn k v)))
|
||||||
|
coll
|
||||||
|
coll)))
|
||||||
|
|
||||||
(defn removev
|
(defn removev
|
||||||
"Returns a vector of the items in coll for which (fn item) returns logical false"
|
"Returns a vector of the items in coll for which (fn item) returns logical false"
|
||||||
|
|
36
common/src/app/common/debug.clj
Normal file
36
common/src/app/common/debug.clj
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
;;
|
||||||
|
;; Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
(ns app.common.debug
|
||||||
|
(:require
|
||||||
|
[app.common.logging :as l]
|
||||||
|
[app.common.pprint :as pp]))
|
||||||
|
|
||||||
|
(defn pprint
|
||||||
|
[expr]
|
||||||
|
(l/raw! :debug
|
||||||
|
(binding [*print-level* pp/default-level
|
||||||
|
*print-length* pp/default-length]
|
||||||
|
(with-out-str
|
||||||
|
(println "tap dbg:")
|
||||||
|
(pp/pprint expr {:max-width pp/default-width})))))
|
||||||
|
|
||||||
|
|
||||||
|
(def store (atom {}))
|
||||||
|
|
||||||
|
(defn get-stored
|
||||||
|
[]
|
||||||
|
(deref store))
|
||||||
|
|
||||||
|
(defn tap-handler
|
||||||
|
[v]
|
||||||
|
(if (and (vector? v)
|
||||||
|
(keyword (first v)))
|
||||||
|
(let [[command obj] v]
|
||||||
|
(case command
|
||||||
|
(:print :prn :pprint) (pprint obj)
|
||||||
|
:store (reset! store obj)))
|
||||||
|
(pprint v)))
|
|
@ -6,4 +6,4 @@
|
||||||
|
|
||||||
(ns app.common.files.defaults)
|
(ns app.common.files.defaults)
|
||||||
|
|
||||||
(def version 44)
|
(def version 46)
|
||||||
|
|
|
@ -484,7 +484,7 @@
|
||||||
(letfn [(red-fn [cur-idx id]
|
(letfn [(red-fn [cur-idx id]
|
||||||
(let [[prev-idx _] (first cur-idx)
|
(let [[prev-idx _] (first cur-idx)
|
||||||
prev-idx (or prev-idx 0)
|
prev-idx (or prev-idx 0)
|
||||||
cur-idx (conj cur-idx [(inc prev-idx) id])]
|
cur-idx (conj cur-idx (d/vec2 (inc prev-idx) id))]
|
||||||
(rec-index cur-idx id)))
|
(rec-index cur-idx id)))
|
||||||
(rec-index [cur-idx id]
|
(rec-index [cur-idx id]
|
||||||
(let [object (get objects id)]
|
(let [object (get objects id)]
|
||||||
|
@ -509,10 +509,11 @@
|
||||||
|
|
||||||
(defn order-by-indexed-shapes
|
(defn order-by-indexed-shapes
|
||||||
[objects ids]
|
[objects ids]
|
||||||
|
(let [ids (if (set? ids) ids (set ids))]
|
||||||
(->> (indexed-shapes objects)
|
(->> (indexed-shapes objects)
|
||||||
(sort-by first)
|
(filter (fn [o] (contains? ids (val o))))
|
||||||
(filter (comp (into #{} ids) second))
|
(sort-by key)
|
||||||
(map second)))
|
(map val))))
|
||||||
|
|
||||||
(defn get-index-replacement
|
(defn get-index-replacement
|
||||||
"Given a collection of shapes, calculate their positions
|
"Given a collection of shapes, calculate their positions
|
||||||
|
@ -542,6 +543,11 @@
|
||||||
[path-vec]
|
[path-vec]
|
||||||
(str/join " / " path-vec))
|
(str/join " / " path-vec))
|
||||||
|
|
||||||
|
(defn join-path-with-dot
|
||||||
|
"Regenerate a path as a string, from a vector."
|
||||||
|
[path-vec]
|
||||||
|
(str/join "\u00A0\u2022\u00A0" path-vec))
|
||||||
|
|
||||||
(defn clean-path
|
(defn clean-path
|
||||||
"Remove empty items from the path."
|
"Remove empty items from the path."
|
||||||
[path]
|
[path]
|
||||||
|
@ -607,6 +613,14 @@
|
||||||
""
|
""
|
||||||
(join-path (butlast split)))))
|
(join-path (butlast split)))))
|
||||||
|
|
||||||
|
(defn butlast-path-with-dots
|
||||||
|
"Remove the last item of the path."
|
||||||
|
[path]
|
||||||
|
(let [split (split-path path)]
|
||||||
|
(if (= 1 (count split))
|
||||||
|
""
|
||||||
|
(join-path-with-dot (butlast split)))))
|
||||||
|
|
||||||
(defn last-path
|
(defn last-path
|
||||||
"Returns the last item of the path."
|
"Returns the last item of the path."
|
||||||
[path]
|
[path]
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
(ns app.common.files.libraries-helpers
|
(ns app.common.files.libraries-helpers
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data :as d]
|
||||||
[app.common.files.changes-builder :as pcb]
|
[app.common.files.changes-builder :as pcb]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.types.component :as ctk]
|
[app.common.types.component :as ctk]
|
||||||
|
@ -37,41 +38,50 @@
|
||||||
use it as root. Otherwise, create a frame (v2) or group (v1) that contains all ids. Then, make a
|
use it as root. Otherwise, create a frame (v2) or group (v1) that contains all ids. Then, make a
|
||||||
component with it, and link all shapes to their corresponding one in the component."
|
component with it, and link all shapes to their corresponding one in the component."
|
||||||
[it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board]
|
[it shapes objects page-id file-id components-v2 prepare-create-group prepare-create-board]
|
||||||
|
|
||||||
(let [changes (pcb/empty-changes it page-id)
|
(let [changes (pcb/empty-changes it page-id)
|
||||||
|
shapes-count (count shapes)
|
||||||
|
first-shape (first shapes)
|
||||||
|
|
||||||
|
from-singe-frame?
|
||||||
|
(and (= 1 shapes-count)
|
||||||
|
(cfh/frame-shape? first-shape))
|
||||||
|
|
||||||
from-singe-frame? (and (= 1 (count shapes)) (-> shapes first cfh/frame-shape?))
|
|
||||||
[root changes old-root-ids]
|
[root changes old-root-ids]
|
||||||
(if (and (= (count shapes) 1)
|
(if (and (= shapes-count 1)
|
||||||
(or (and (= (:type (first shapes)) :group) (not components-v2))
|
(or (and (cfh/group-shape? first-shape)
|
||||||
(= (:type (first shapes)) :frame))
|
(not components-v2))
|
||||||
(not (ctk/instance-head? (first shapes))))
|
(cfh/frame-shape? first-shape))
|
||||||
|
(not (ctk/instance-head? first-shape)))
|
||||||
[(first shapes)
|
[first-shape
|
||||||
(-> (pcb/empty-changes it page-id)
|
(-> (pcb/empty-changes it page-id)
|
||||||
(pcb/with-objects objects))
|
(pcb/with-objects objects))
|
||||||
(:shapes (first shapes))]
|
(:shapes first-shape)]
|
||||||
|
|
||||||
(let [root-name (if (= 1 (count shapes))
|
(let [root-name (if (= 1 shapes-count)
|
||||||
(:name (first shapes))
|
(:name first-shape)
|
||||||
"Component 1")
|
"Component 1")
|
||||||
|
|
||||||
[root changes] (if-not components-v2
|
shape-ids (into (d/ordered-set) (map :id) shapes)
|
||||||
|
|
||||||
|
[root changes]
|
||||||
|
(if-not components-v2
|
||||||
(prepare-create-group it ; These functions needs to be passed as argument
|
(prepare-create-group it ; These functions needs to be passed as argument
|
||||||
objects ; to avoid a circular dependence
|
objects ; to avoid a circular dependence
|
||||||
page-id
|
page-id
|
||||||
shapes
|
shapes
|
||||||
root-name
|
root-name
|
||||||
(not (ctk/instance-head? (first shapes))))
|
(not (ctk/instance-head? first-shape)))
|
||||||
(prepare-create-board changes
|
(prepare-create-board changes
|
||||||
(uuid/next)
|
(uuid/next)
|
||||||
(:parent-id (first shapes))
|
(:parent-id first-shape)
|
||||||
objects
|
objects
|
||||||
(map :id shapes)
|
shape-ids
|
||||||
nil
|
nil
|
||||||
root-name
|
root-name
|
||||||
true))]
|
true))]
|
||||||
|
|
||||||
[root changes (map :id shapes)]))
|
[root changes shape-ids]))
|
||||||
|
|
||||||
changes
|
changes
|
||||||
(cond-> changes
|
(cond-> changes
|
||||||
|
@ -79,8 +89,7 @@
|
||||||
(pcb/update-shapes
|
(pcb/update-shapes
|
||||||
(:shapes root)
|
(:shapes root)
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
(-> shape
|
(assoc shape :constraints-h :scale :constraints-v :scale))))
|
||||||
(assoc :constraints-h :scale :constraints-v :scale)))))
|
|
||||||
|
|
||||||
objects' (assoc objects (:id root) root)
|
objects' (assoc objects (:id root) root)
|
||||||
|
|
||||||
|
|
|
@ -109,11 +109,14 @@
|
||||||
(assoc :points (grc/rect->points selrect))))))
|
(assoc :points (grc/rect->points selrect))))))
|
||||||
|
|
||||||
(fix-empty-points [shape]
|
(fix-empty-points [shape]
|
||||||
(let [shape (cond-> shape
|
(if (empty? (:points shape))
|
||||||
(empty? (:selrect shape)) (cts/setup-rect))]
|
(-> shape
|
||||||
(cond-> shape
|
(update :selrect (fn [selrect]
|
||||||
(empty? (:points shape))
|
(if (map? selrect)
|
||||||
(assoc :points (grc/rect->points (:selrect shape))))))
|
(grc/make-rect selrect)
|
||||||
|
selrect)))
|
||||||
|
(cts/setup-shape))
|
||||||
|
shape))
|
||||||
|
|
||||||
(update-object [object]
|
(update-object [object]
|
||||||
(cond-> object
|
(cond-> object
|
||||||
|
@ -620,6 +623,10 @@
|
||||||
(-> object
|
(-> object
|
||||||
(assoc :parent-id uuid/zero)
|
(assoc :parent-id uuid/zero)
|
||||||
(assoc :frame-id uuid/zero)
|
(assoc :frame-id uuid/zero)
|
||||||
|
;; We explicitly dissoc them and let the shape-setup
|
||||||
|
;; to regenerate it with valid values.
|
||||||
|
(dissoc :selrect)
|
||||||
|
(dissoc :points)
|
||||||
(cts/setup-shape))
|
(cts/setup-shape))
|
||||||
object))
|
object))
|
||||||
|
|
||||||
|
@ -843,3 +850,29 @@
|
||||||
(-> data
|
(-> data
|
||||||
(update :pages-index update-vals update-container)
|
(update :pages-index update-vals update-container)
|
||||||
(update :components update-vals update-container))))
|
(update :components update-vals update-container))))
|
||||||
|
|
||||||
|
(defmethod migrate 45
|
||||||
|
[data]
|
||||||
|
(letfn [(fix-shape [shape]
|
||||||
|
(let [frame-id (or (:frame-id shape)
|
||||||
|
uuid/zero)
|
||||||
|
parent-id (or (:parent-id shape)
|
||||||
|
frame-id)]
|
||||||
|
(assoc shape :frame-id frame-id
|
||||||
|
:parent-id parent-id)))
|
||||||
|
|
||||||
|
(update-container [container]
|
||||||
|
(d/update-when container :objects update-vals fix-shape))]
|
||||||
|
(-> data
|
||||||
|
(update :pages-index update-vals update-container))))
|
||||||
|
|
||||||
|
(defmethod migrate 46
|
||||||
|
[data]
|
||||||
|
(letfn [(update-object [object]
|
||||||
|
(dissoc object :thumbnail))
|
||||||
|
|
||||||
|
(update-container [container]
|
||||||
|
(d/update-when container :objects update-vals update-object))]
|
||||||
|
(-> data
|
||||||
|
(update :pages-index update-vals update-container)
|
||||||
|
(update :components update-vals update-container))))
|
||||||
|
|
|
@ -39,16 +39,17 @@
|
||||||
|
|
||||||
(defn prepare-move-shapes-into-frame
|
(defn prepare-move-shapes-into-frame
|
||||||
[changes frame-id shapes objects]
|
[changes frame-id shapes objects]
|
||||||
(let [ordered-indexes (cfh/order-by-indexed-shapes objects shapes)
|
(let [parent-id (dm/get-in objects [frame-id :parent-id])
|
||||||
parent-id (get-in objects [frame-id :parent-id])
|
shapes (remove #(= % parent-id) shapes)
|
||||||
ordered-indexes (->> ordered-indexes (remove #(= % parent-id)))
|
to-move (->> shapes
|
||||||
to-move-shapes (map (d/getf objects) ordered-indexes)]
|
(map (d/getf objects))
|
||||||
(if (d/not-empty? to-move-shapes)
|
(not-empty))]
|
||||||
|
(if to-move
|
||||||
(-> changes
|
(-> changes
|
||||||
(cond-> (not (ctl/any-layout? objects frame-id))
|
(cond-> (not (ctl/any-layout? objects frame-id))
|
||||||
(pcb/update-shapes ordered-indexes ctl/remove-layout-item-data))
|
(pcb/update-shapes shapes ctl/remove-layout-item-data))
|
||||||
(pcb/update-shapes ordered-indexes #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true)))
|
(pcb/update-shapes shapes #(cond-> % (cfh/frame-shape? %) (assoc :hide-in-viewer true)))
|
||||||
(pcb/change-parent frame-id to-move-shapes 0)
|
(pcb/change-parent frame-id to-move 0)
|
||||||
(cond-> (ctl/grid-layout? objects frame-id)
|
(cond-> (ctl/grid-layout? objects frame-id)
|
||||||
(-> (pcb/update-shapes [frame-id] ctl/assign-cells {:with-objects? true})
|
(-> (pcb/update-shapes [frame-id] ctl/assign-cells {:with-objects? true})
|
||||||
(pcb/reorder-grid-children [frame-id]))))
|
(pcb/reorder-grid-children [frame-id]))))
|
||||||
|
@ -60,22 +61,31 @@
|
||||||
changes id parent-id objects selected index frame-name without-fill? nil))
|
changes id parent-id objects selected index frame-name without-fill? nil))
|
||||||
|
|
||||||
([changes id parent-id objects selected index frame-name without-fill? target-cell-id]
|
([changes id parent-id objects selected index frame-name without-fill? target-cell-id]
|
||||||
(let [selected-objs (map #(get objects %) selected)
|
(when-let [selected-objs (->> selected
|
||||||
new-index (or index
|
(map (d/getf objects))
|
||||||
(cfh/get-index-replacement selected objects))]
|
(not-empty))]
|
||||||
(when (d/not-empty? selected)
|
|
||||||
(let [srect (gsh/shapes->rect selected-objs)
|
|
||||||
selected-id (first selected)
|
|
||||||
|
|
||||||
frame-id (dm/get-in objects [selected-id :frame-id])
|
(let [;; We calculate here the ordered selection because it is used
|
||||||
parent-id (or parent-id (dm/get-in objects [selected-id :parent-id]))
|
;; multiple times and this avoid the need of creating the index
|
||||||
|
;; manytimes for single operation.
|
||||||
|
selected' (cfh/order-by-indexed-shapes objects selected)
|
||||||
|
new-index (or index
|
||||||
|
(->> (first selected')
|
||||||
|
(cfh/get-position-on-parent objects)
|
||||||
|
(inc)))
|
||||||
|
|
||||||
|
srect (gsh/shapes->rect selected-objs)
|
||||||
|
selected-id (first selected)
|
||||||
|
selected-obj (get objects selected-id)
|
||||||
|
|
||||||
|
frame-id (get selected-obj :frame-id)
|
||||||
|
parent-id (or parent-id (get selected-obj :parent-id))
|
||||||
base-parent (get objects parent-id)
|
base-parent (get objects parent-id)
|
||||||
|
|
||||||
layout-props
|
layout-props
|
||||||
(when (and (= 1 (count selected))
|
(when (and (= 1 (count selected))
|
||||||
(ctl/any-layout? base-parent))
|
(ctl/any-layout? base-parent))
|
||||||
(let [shape (get objects selected-id)]
|
(select-keys selected-obj ctl/layout-item-props))
|
||||||
(select-keys shape ctl/layout-item-props)))
|
|
||||||
|
|
||||||
target-cell-id
|
target-cell-id
|
||||||
(if (and (nil? target-cell-id)
|
(if (and (nil? target-cell-id)
|
||||||
|
@ -88,13 +98,15 @@
|
||||||
:id))
|
:id))
|
||||||
target-cell-id)
|
target-cell-id)
|
||||||
|
|
||||||
attrs {:type :frame
|
attrs
|
||||||
|
{:type :frame
|
||||||
:x (:x srect)
|
:x (:x srect)
|
||||||
:y (:y srect)
|
:y (:y srect)
|
||||||
:width (:width srect)
|
:width (:width srect)
|
||||||
:height (:height srect)}
|
:height (:height srect)}
|
||||||
|
|
||||||
shape (cts/setup-shape
|
shape
|
||||||
|
(cts/setup-shape
|
||||||
(cond-> attrs
|
(cond-> attrs
|
||||||
(some? id)
|
(some? id)
|
||||||
(assoc :id id)
|
(assoc :id id)
|
||||||
|
@ -113,13 +125,14 @@
|
||||||
(or (not= frame-id uuid/zero) without-fill?)
|
(or (not= frame-id uuid/zero) without-fill?)
|
||||||
(assoc :fills [] :hide-in-viewer true)))
|
(assoc :fills [] :hide-in-viewer true)))
|
||||||
|
|
||||||
shape (with-meta shape {:index new-index})
|
shape
|
||||||
|
(with-meta shape {:index new-index})
|
||||||
|
|
||||||
[shape changes]
|
[shape changes]
|
||||||
(prepare-add-shape changes shape objects)
|
(prepare-add-shape changes shape objects)
|
||||||
|
|
||||||
changes
|
changes
|
||||||
(prepare-move-shapes-into-frame changes (:id shape) selected objects)
|
(prepare-move-shapes-into-frame changes (:id shape) selected' objects)
|
||||||
|
|
||||||
changes
|
changes
|
||||||
(cond-> changes
|
(cond-> changes
|
||||||
|
@ -143,7 +156,7 @@
|
||||||
|
|
||||||
(pcb/reorder-grid-children [(:parent-id shape)])))]
|
(pcb/reorder-grid-children [(:parent-id shape)])))]
|
||||||
|
|
||||||
[shape changes])))))
|
[shape changes]))))
|
||||||
|
|
||||||
|
|
||||||
(defn prepare-create-empty-artboard
|
(defn prepare-create-empty-artboard
|
||||||
|
|
|
@ -98,7 +98,8 @@
|
||||||
(defn- check-geometry
|
(defn- check-geometry
|
||||||
"Validate that the shape has valid coordinates, selrect and points."
|
"Validate that the shape has valid coordinates, selrect and points."
|
||||||
[shape file page]
|
[shape file page]
|
||||||
(when (and (not (#{:path :bool} (:type shape)))
|
(when (and (not (or (cfh/path-shape? shape)
|
||||||
|
(cfh/bool-shape? shape)))
|
||||||
(or (nil? (:x shape)) ; This may occur in root shape (uuid/zero) in old files
|
(or (nil? (:x shape)) ; This may occur in root shape (uuid/zero) in old files
|
||||||
(nil? (:y shape))
|
(nil? (:y shape))
|
||||||
(nil? (:width shape))
|
(nil? (:width shape))
|
||||||
|
@ -112,61 +113,64 @@
|
||||||
(defn- check-parent-children
|
(defn- check-parent-children
|
||||||
"Validate parent and children exists, and the link is bidirectional."
|
"Validate parent and children exists, and the link is bidirectional."
|
||||||
[shape file page]
|
[shape file page]
|
||||||
(let [parent (ctst/get-shape page (:parent-id shape))]
|
(let [parent (ctst/get-shape page (:parent-id shape))
|
||||||
|
shape-id (:id shape)
|
||||||
|
shapes (:shapes shape)]
|
||||||
|
|
||||||
(if (nil? parent)
|
(if (nil? parent)
|
||||||
(report-error :parent-not-found
|
(report-error :parent-not-found
|
||||||
(str/ffmt "Parent % not found" (:parent-id shape))
|
(str/ffmt "Parent % not found" (:parent-id shape))
|
||||||
shape file page)
|
shape file page)
|
||||||
(do
|
(do
|
||||||
(when-not (cfh/root? shape)
|
(when-not (cfh/root? shape)
|
||||||
(when-not (some #{(:id shape)} (:shapes parent))
|
(when-not (some #(= shape-id %) (:shapes parent))
|
||||||
(report-error :child-not-in-parent
|
(report-error :child-not-in-parent
|
||||||
(str/ffmt "Shape % not in parent's children list" (:id shape))
|
(str/ffmt "Shape % not in parent's children list" shape-id)
|
||||||
shape file page)))
|
shape file page)))
|
||||||
|
|
||||||
(when-not (= (count (:shapes shape)) (count (distinct (:shapes shape))))
|
(when-not (= (count shapes) (count (distinct shapes)))
|
||||||
(report-error :duplicated-children
|
(report-error :duplicated-children
|
||||||
(str/ffmt "Shape % has duplicated children" (:id shape))
|
(str/ffmt "Shape % has duplicated children" shape-id)
|
||||||
shape file page))
|
shape file page))
|
||||||
|
|
||||||
(doseq [child-id (:shapes shape)]
|
(doseq [child-id shapes]
|
||||||
(let [child (ctst/get-shape page child-id)]
|
(let [child (ctst/get-shape page child-id)]
|
||||||
(if (nil? child)
|
(if (nil? child)
|
||||||
(report-error :child-not-found
|
(report-error :child-not-found
|
||||||
(str/ffmt "Child % not found in parent %" child-id (:id shape))
|
(str/ffmt "Child % not found in parent %" child-id shape-id)
|
||||||
shape file page
|
shape file page
|
||||||
:parent-id (:id shape)
|
:parent-id shape-id
|
||||||
:child-id child-id)
|
:child-id child-id)
|
||||||
(when (not= (:parent-id child) (:id shape))
|
(when (not= (:parent-id child) shape-id)
|
||||||
(report-error :invalid-parent
|
(report-error :invalid-parent
|
||||||
(str/ffmt "Child % has invalid parent %" child-id (:id shape))
|
(str/ffmt "Child % has invalid parent %" child-id shape-id)
|
||||||
child file page
|
child file page
|
||||||
:parent-id (:id shape))))))))))
|
:parent-id shape-id)))))))))
|
||||||
|
|
||||||
(defn- check-frame
|
(defn- check-frame
|
||||||
"Validate that the frame-id shape exists and is indeed a frame. Also
|
"Validate that the frame-id shape exists and is indeed a frame. Also
|
||||||
it must point to the parent shape (if this is a frame) or to the
|
it must point to the parent shape (if this is a frame) or to the
|
||||||
frame-id of the parent (if not)."
|
frame-id of the parent (if not)."
|
||||||
[shape file page]
|
[{:keys [frame-id] :as shape} file page]
|
||||||
(let [frame (ctst/get-shape page (:frame-id shape))]
|
(let [frame (ctst/get-shape page frame-id)]
|
||||||
(if (nil? frame)
|
(if (nil? frame)
|
||||||
(report-error :frame-not-found
|
(report-error :frame-not-found
|
||||||
(str/ffmt "Frame % not found" (:frame-id shape))
|
(str/ffmt "Frame % not found" frame-id)
|
||||||
shape file page)
|
shape file page)
|
||||||
(if (not= (:type frame) :frame)
|
(if (not= (:type frame) :frame)
|
||||||
(report-error :invalid-frame
|
(report-error :invalid-frame
|
||||||
(str/ffmt "Frame % is not actually a frame" (:frame-id shape))
|
(str/ffmt "Frame % is not actually a frame" frame-id)
|
||||||
shape file page)
|
shape file page)
|
||||||
(let [parent (ctst/get-shape page (:parent-id shape))]
|
(let [parent (ctst/get-shape page (:parent-id shape))]
|
||||||
(when (some? parent)
|
(when (some? parent)
|
||||||
(if (= (:type parent) :frame)
|
(if (= (:type parent) :frame)
|
||||||
(when-not (= (:frame-id shape) (:id parent))
|
(when-not (= frame-id (:id parent))
|
||||||
(report-error :invalid-frame
|
(report-error :invalid-frame
|
||||||
(str/ffmt "Frame-id should point to parent %" (:id parent))
|
(str/ffmt "Frame-id should point to parent %" (:id parent))
|
||||||
shape file page))
|
shape file page))
|
||||||
(when-not (= (:frame-id shape) (:frame-id parent))
|
(when-not (= frame-id (:frame-id parent))
|
||||||
(report-error :invalid-frame
|
(report-error :invalid-frame
|
||||||
(str/ffmt "Frame-id should point to parent frame %" (:frame-id parent))
|
(str/ffmt "Frame-id should point to parent frame %" frame-id)
|
||||||
shape file page)))))))))
|
shape file page)))))))))
|
||||||
|
|
||||||
(defn- check-component-main-head
|
(defn- check-component-main-head
|
||||||
|
@ -289,8 +293,7 @@
|
||||||
(check-component-main-head shape file page libraries)
|
(check-component-main-head shape file page libraries)
|
||||||
(check-component-root shape file page)
|
(check-component-root shape file page)
|
||||||
(check-component-not-ref shape file page)
|
(check-component-not-ref shape file page)
|
||||||
(doseq [child-id (:shapes shape)]
|
(run! #(check-shape % file page libraries :context :main-top) (:shapes shape)))
|
||||||
(check-shape child-id file page libraries :context :main-top)))
|
|
||||||
|
|
||||||
(defn- check-shape-main-root-nested
|
(defn- check-shape-main-root-nested
|
||||||
"Root shape of a nested main instance
|
"Root shape of a nested main instance
|
||||||
|
@ -301,8 +304,7 @@
|
||||||
(check-component-main-head shape file page libraries)
|
(check-component-main-head shape file page libraries)
|
||||||
(check-component-not-root shape file page)
|
(check-component-not-root shape file page)
|
||||||
(check-component-not-ref shape file page)
|
(check-component-not-ref shape file page)
|
||||||
(doseq [child-id (:shapes shape)]
|
(run! #(check-shape % file page libraries :context :main-nested) (:shapes shape)))
|
||||||
(check-shape child-id file page libraries :context :main-nested)))
|
|
||||||
|
|
||||||
(defn- check-shape-copy-root-top
|
(defn- check-shape-copy-root-top
|
||||||
"Root shape of a top copy instance
|
"Root shape of a top copy instance
|
||||||
|
@ -314,8 +316,7 @@
|
||||||
(check-component-not-main-head shape file page libraries)
|
(check-component-not-main-head shape file page libraries)
|
||||||
(check-component-root shape file page)
|
(check-component-root shape file page)
|
||||||
(check-component-ref shape file page libraries)
|
(check-component-ref shape file page libraries)
|
||||||
(doseq [child-id (:shapes shape)]
|
(run! #(check-shape % file page libraries :context :copy-top) (:shapes shape)))
|
||||||
(check-shape child-id file page libraries :context :copy-top)))
|
|
||||||
|
|
||||||
(defn- check-shape-copy-root-nested
|
(defn- check-shape-copy-root-nested
|
||||||
"Root shape of a nested copy instance
|
"Root shape of a nested copy instance
|
||||||
|
@ -326,8 +327,7 @@
|
||||||
(check-component-not-main-head shape file page libraries)
|
(check-component-not-main-head shape file page libraries)
|
||||||
(check-component-not-root shape file page)
|
(check-component-not-root shape file page)
|
||||||
(check-component-ref shape file page libraries)
|
(check-component-ref shape file page libraries)
|
||||||
(doseq [child-id (:shapes shape)]
|
(run! #(check-shape % file page libraries :context :copy-nested) (:shapes shape)))
|
||||||
(check-shape child-id file page libraries :context :copy-nested)))
|
|
||||||
|
|
||||||
(defn- check-shape-main-not-root
|
(defn- check-shape-main-not-root
|
||||||
"Not-root shape of a main instance (not any attribute)"
|
"Not-root shape of a main instance (not any attribute)"
|
||||||
|
@ -335,8 +335,7 @@
|
||||||
(check-component-not-main-not-head shape file page)
|
(check-component-not-main-not-head shape file page)
|
||||||
(check-component-not-root shape file page)
|
(check-component-not-root shape file page)
|
||||||
(check-component-not-ref shape file page)
|
(check-component-not-ref shape file page)
|
||||||
(doseq [child-id (:shapes shape)]
|
(run! #(check-shape % file page libraries :context :main-any) (:shapes shape)))
|
||||||
(check-shape child-id file page libraries :context :main-any)))
|
|
||||||
|
|
||||||
(defn- check-shape-copy-not-root
|
(defn- check-shape-copy-not-root
|
||||||
"Not-root shape of a copy instance :shape-ref"
|
"Not-root shape of a copy instance :shape-ref"
|
||||||
|
@ -344,8 +343,7 @@
|
||||||
(check-component-not-main-not-head shape file page)
|
(check-component-not-main-not-head shape file page)
|
||||||
(check-component-not-root shape file page)
|
(check-component-not-root shape file page)
|
||||||
(check-component-ref shape file page libraries)
|
(check-component-ref shape file page libraries)
|
||||||
(doseq [child-id (:shapes shape)]
|
(run! #(check-shape % file page libraries :context :copy-any) (:shapes shape)))
|
||||||
(check-shape child-id file page libraries :context :copy-any)))
|
|
||||||
|
|
||||||
(defn- check-shape-not-component
|
(defn- check-shape-not-component
|
||||||
"Shape is not in a component or is a fostered children (not any
|
"Shape is not in a component or is a fostered children (not any
|
||||||
|
@ -354,8 +352,7 @@
|
||||||
(check-component-not-main-not-head shape file page)
|
(check-component-not-main-not-head shape file page)
|
||||||
(check-component-not-root shape file page)
|
(check-component-not-root shape file page)
|
||||||
(check-component-not-ref shape file page)
|
(check-component-not-ref shape file page)
|
||||||
(doseq [child-id (:shapes shape)]
|
(run! #(check-shape % file page libraries :context :not-component) (:shapes shape)))
|
||||||
(check-shape child-id file page libraries :context :not-component)))
|
|
||||||
|
|
||||||
(defn- check-shape
|
(defn- check-shape
|
||||||
"Validate referential integrity and semantic coherence of
|
"Validate referential integrity and semantic coherence of
|
||||||
|
@ -439,6 +436,11 @@
|
||||||
"Objects list cannot be nil"
|
"Objects list cannot be nil"
|
||||||
component file nil)))
|
component file nil)))
|
||||||
|
|
||||||
|
(defn- get-orphan-shapes
|
||||||
|
[{:keys [objects] :as page}]
|
||||||
|
(let [xf (comp (map #(contains? objects (:parent-id %)))
|
||||||
|
(map :id))]
|
||||||
|
(into [] xf (vals objects))))
|
||||||
|
|
||||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||||
;; PUBLIC API: VALIDATION FUNCTIONS
|
;; PUBLIC API: VALIDATION FUNCTIONS
|
||||||
|
@ -451,18 +453,14 @@
|
||||||
[{:keys [data features] :as file} libraries]
|
[{:keys [data features] :as file} libraries]
|
||||||
(when (contains? features "components/v2")
|
(when (contains? features "components/v2")
|
||||||
(binding [*errors* (volatile! [])]
|
(binding [*errors* (volatile! [])]
|
||||||
(doseq [page (filter :id (ctpl/pages-seq data))]
|
|
||||||
(let [orphans (->> page
|
|
||||||
:objects
|
|
||||||
vals
|
|
||||||
(filter #(not (contains? (:objects page) (:parent-id %))))
|
|
||||||
(map :id))]
|
|
||||||
(check-shape uuid/zero file page libraries)
|
|
||||||
(doseq [shape-id orphans]
|
|
||||||
(check-shape shape-id file page libraries))))
|
|
||||||
|
|
||||||
(doseq [component (vals (:components data))]
|
(doseq [page (filter :id (ctpl/pages-seq data))]
|
||||||
(check-component component file))
|
(check-shape uuid/zero file page libraries)
|
||||||
|
(->> (get-orphan-shapes page)
|
||||||
|
(run! #(check-shape % file page libraries))))
|
||||||
|
|
||||||
|
(->> (vals (:components data))
|
||||||
|
(run! #(check-component % file)))
|
||||||
|
|
||||||
(-> *errors* deref not-empty))))
|
(-> *errors* deref not-empty))))
|
||||||
|
|
||||||
|
|
|
@ -67,7 +67,8 @@
|
||||||
([a b c d e f]
|
([a b c d e f]
|
||||||
(pos->Matrix a b c d e f)))
|
(pos->Matrix a b c d e f)))
|
||||||
|
|
||||||
(def number-regex #"[+-]?\d*(\.\d+)?(e[+-]?\d+)?")
|
(def number-regex
|
||||||
|
#"[+-]?\d*(\.\d+)?([eE][+-]?\d+)?")
|
||||||
|
|
||||||
(defn str->matrix
|
(defn str->matrix
|
||||||
[matrix-str]
|
[matrix-str]
|
||||||
|
@ -76,8 +77,8 @@
|
||||||
(map (comp d/parse-double first)))]
|
(map (comp d/parse-double first)))]
|
||||||
(apply matrix params)))
|
(apply matrix params)))
|
||||||
|
|
||||||
(sm/def! ::matrix-map
|
(def ^:private schema:matrix-attrs
|
||||||
[:map {:title "MatrixMap"}
|
[:map {:title "MatrixAttrs"}
|
||||||
[:a ::sm/safe-double]
|
[:a ::sm/safe-double]
|
||||||
[:b ::sm/safe-double]
|
[:b ::sm/safe-double]
|
||||||
[:c ::sm/safe-double]
|
[:c ::sm/safe-double]
|
||||||
|
@ -85,6 +86,10 @@
|
||||||
[:e ::sm/safe-double]
|
[:e ::sm/safe-double]
|
||||||
[:f ::sm/safe-double]])
|
[:f ::sm/safe-double]])
|
||||||
|
|
||||||
|
(def valid-matrix?
|
||||||
|
(sm/lazy-validator
|
||||||
|
[:and [:fn matrix?] schema:matrix-attrs]))
|
||||||
|
|
||||||
(sm/def! ::matrix
|
(sm/def! ::matrix
|
||||||
(letfn [(decode [o]
|
(letfn [(decode [o]
|
||||||
(if (map? o)
|
(if (map? o)
|
||||||
|
@ -101,7 +106,7 @@
|
||||||
(dm/get-prop o :f) ","))]
|
(dm/get-prop o :f) ","))]
|
||||||
|
|
||||||
{:type ::matrix
|
{:type ::matrix
|
||||||
:pred matrix?
|
:pred valid-matrix?
|
||||||
:type-properties
|
:type-properties
|
||||||
{:title "matrix"
|
{:title "matrix"
|
||||||
:description "Matrix instance"
|
:description "Matrix instance"
|
||||||
|
|
|
@ -41,12 +41,6 @@
|
||||||
[v]
|
[v]
|
||||||
(instance? Point v))
|
(instance? Point v))
|
||||||
|
|
||||||
(sm/def! ::point-map
|
|
||||||
[:map {:title "PointMap"}
|
|
||||||
[:x ::sm/safe-number]
|
|
||||||
[:y ::sm/safe-number]])
|
|
||||||
|
|
||||||
|
|
||||||
;; FIXME: deprecated
|
;; FIXME: deprecated
|
||||||
(s/def ::x ::us/safe-number)
|
(s/def ::x ::us/safe-number)
|
||||||
(s/def ::y ::us/safe-number)
|
(s/def ::y ::us/safe-number)
|
||||||
|
@ -57,6 +51,16 @@
|
||||||
(s/def ::point
|
(s/def ::point
|
||||||
(s/and ::point-attrs point?))
|
(s/and ::point-attrs point?))
|
||||||
|
|
||||||
|
|
||||||
|
(def ^:private schema:point-attrs
|
||||||
|
[:map {:title "PointAttrs"}
|
||||||
|
[:x ::sm/safe-number]
|
||||||
|
[:y ::sm/safe-number]])
|
||||||
|
|
||||||
|
(def valid-point?
|
||||||
|
(sm/lazy-validator
|
||||||
|
[:and [:fn point?] schema:point-attrs]))
|
||||||
|
|
||||||
(sm/def! ::point
|
(sm/def! ::point
|
||||||
(letfn [(decode [p]
|
(letfn [(decode [p]
|
||||||
(if (map? p)
|
(if (map? p)
|
||||||
|
@ -71,7 +75,7 @@
|
||||||
(dm/get-prop p :y)))]
|
(dm/get-prop p :y)))]
|
||||||
|
|
||||||
{:type ::point
|
{:type ::point
|
||||||
:pred point?
|
:pred valid-point?
|
||||||
:type-properties
|
:type-properties
|
||||||
{:title "point"
|
{:title "point"
|
||||||
:description "Point"
|
:description "Point"
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.record :as rc]
|
[app.common.record :as rc]
|
||||||
|
[app.common.schema :as sm]
|
||||||
|
[app.common.schema.generators :as sg]
|
||||||
[app.common.transit :as t]))
|
[app.common.transit :as t]))
|
||||||
|
|
||||||
(rc/defrecord Rect [x y width height x1 y1 x2 y2])
|
(rc/defrecord Rect [x y width height x1 y1 x2 y2])
|
||||||
|
@ -66,6 +68,31 @@
|
||||||
h (mth/max height 0.01)]
|
h (mth/max height 0.01)]
|
||||||
(pos->Rect x y w h x y (+ x w) (+ y h))))))
|
(pos->Rect x y w h x y (+ x w) (+ y h))))))
|
||||||
|
|
||||||
|
(def ^:private schema:rect-attrs
|
||||||
|
[:map {:title "RectAttrs"}
|
||||||
|
[:x ::sm/safe-number]
|
||||||
|
[:y ::sm/safe-number]
|
||||||
|
[:width ::sm/safe-number]
|
||||||
|
[:height ::sm/safe-number]
|
||||||
|
[:x1 ::sm/safe-number]
|
||||||
|
[:y1 ::sm/safe-number]
|
||||||
|
[:x2 ::sm/safe-number]
|
||||||
|
[:y2 ::sm/safe-number]])
|
||||||
|
|
||||||
|
(sm/define! ::rect
|
||||||
|
[:and
|
||||||
|
{:gen/gen (->> (sg/tuple (sg/small-double)
|
||||||
|
(sg/small-double)
|
||||||
|
(sg/small-double)
|
||||||
|
(sg/small-double))
|
||||||
|
(sg/fmap #(apply make-rect %)))}
|
||||||
|
[:fn rect?]
|
||||||
|
schema:rect-attrs])
|
||||||
|
|
||||||
|
(def valid-rect?
|
||||||
|
(sm/lazy-validator
|
||||||
|
[:and [:fn rect?] schema:rect-attrs]))
|
||||||
|
|
||||||
(def empty-rect
|
(def empty-rect
|
||||||
(make-rect 0 0 0.01 0.01))
|
(make-rect 0 0 0.01 0.01))
|
||||||
|
|
||||||
|
|
|
@ -92,5 +92,11 @@
|
||||||
(defn resolve-subtree
|
(defn resolve-subtree
|
||||||
"Resolves the subtree but only partialy from-to the parameters"
|
"Resolves the subtree but only partialy from-to the parameters"
|
||||||
[from-id to-id objects]
|
[from-id to-id objects]
|
||||||
|
(concat
|
||||||
(->> (get-children-seq from-id objects)
|
(->> (get-children-seq from-id objects)
|
||||||
(d/take-until #(= (:id %) to-id))))
|
(d/take-until #(= (:id %) to-id)))
|
||||||
|
|
||||||
|
;; Add the children of `to-id` to the subtree. Rest is to remove the
|
||||||
|
;; to-id element that is already on the previous sequence
|
||||||
|
(->> (get-children-seq to-id objects)
|
||||||
|
rest)))
|
||||||
|
|
|
@ -9,9 +9,26 @@
|
||||||
(:require
|
(:require
|
||||||
[me.flowthing.pp :as pp]))
|
[me.flowthing.pp :as pp]))
|
||||||
|
|
||||||
|
(def default-level 8)
|
||||||
|
(def default-length 25)
|
||||||
|
(def default-width 120)
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(defn set-defaults
|
||||||
|
[& {:keys [level width length]}]
|
||||||
|
(when length
|
||||||
|
(alter-var-root #'default-length (constantly length)))
|
||||||
|
(when width
|
||||||
|
(alter-var-root #'default-width (constantly width)))
|
||||||
|
(when level
|
||||||
|
(alter-var-root #'default-level (constantly level)))
|
||||||
|
nil))
|
||||||
|
|
||||||
(defn pprint
|
(defn pprint
|
||||||
[expr & {:keys [width level length]
|
[expr & {:keys [width level length]
|
||||||
:or {width 120 level 8 length 25}}]
|
:or {width default-width
|
||||||
|
level default-level
|
||||||
|
length default-length}}]
|
||||||
(binding [*print-level* level
|
(binding [*print-level* level
|
||||||
*print-length* length]
|
*print-length* length]
|
||||||
(pp/pprint expr {:max-width width})))
|
(pp/pprint expr {:max-width width})))
|
||||||
|
|
|
@ -895,7 +895,8 @@
|
||||||
|
|
||||||
(defn map-nodes [mapfn node]
|
(defn map-nodes [mapfn node]
|
||||||
(let [update-content
|
(let [update-content
|
||||||
(fn [content] (cond->> content
|
(fn [content]
|
||||||
|
(cond->> content
|
||||||
(vector? content)
|
(vector? content)
|
||||||
(mapv (partial map-nodes mapfn))))]
|
(mapv (partial map-nodes mapfn))))]
|
||||||
|
|
||||||
|
@ -922,7 +923,8 @@
|
||||||
value)))
|
value)))
|
||||||
|
|
||||||
(defn fix-default-values
|
(defn fix-default-values
|
||||||
"Gives values to some SVG elements which defaults won't work when imported into the platform"
|
"Gives values to some SVG elements which defaults won't work when
|
||||||
|
imported into the platform"
|
||||||
[svg-data]
|
[svg-data]
|
||||||
(let [add-defaults
|
(let [add-defaults
|
||||||
(fn [{:keys [tag attrs] :as node}]
|
(fn [{:keys [tag attrs] :as node}]
|
||||||
|
@ -984,29 +986,38 @@
|
||||||
(fix-percent-attrs-viewbox [attrs]
|
(fix-percent-attrs-viewbox [attrs]
|
||||||
(d/mapm fix-percent-attr-viewbox attrs))
|
(d/mapm fix-percent-attr-viewbox attrs))
|
||||||
|
|
||||||
(fix-percent-attr-numeric [_ attr-val]
|
(fix-percent-attr-numeric-val [val]
|
||||||
(let [is-percent? (str/ends-with? attr-val "%")]
|
(let [val (d/parse-double (str/rtrim val "%"))]
|
||||||
(if is-percent?
|
(str (/ val 100))))
|
||||||
(str (let [attr-num (d/parse-double (str/rtrim attr-val "%"))]
|
|
||||||
(/ attr-num 100)))
|
|
||||||
attr-val)))
|
|
||||||
|
|
||||||
(fix-percent-attrs-numeric [attrs]
|
(fix-percent-attr-numeric [attrs key val]
|
||||||
(d/mapm fix-percent-attr-numeric attrs))
|
(cond
|
||||||
|
(= key :style)
|
||||||
|
attrs
|
||||||
|
|
||||||
|
(str/starts-with? (d/name key) "data-")
|
||||||
|
attrs
|
||||||
|
|
||||||
|
(str/ends-with? val "%")
|
||||||
|
(assoc attrs key (fix-percent-attr-numeric-val val))
|
||||||
|
|
||||||
|
:else
|
||||||
|
attrs))
|
||||||
|
|
||||||
(fix-percent-values [node]
|
(fix-percent-values [node]
|
||||||
(let [units (or (get-in node [:attrs :filterUnits])
|
(let [units (or (get-in node [:attrs :filterUnits])
|
||||||
(get-in node [:attrs :gradientUnits])
|
(get-in node [:attrs :gradientUnits])
|
||||||
(get-in node [:attrs :patternUnits])
|
(get-in node [:attrs :patternUnits])
|
||||||
(get-in node [:attrs :clipUnits]))]
|
(get-in node [:attrs :clipUnits]))]
|
||||||
|
|
||||||
(cond-> node
|
(cond-> node
|
||||||
(or (= "objectBoundingBox" units) (nil? units))
|
(or (= "objectBoundingBox" units) (nil? units))
|
||||||
(update :attrs fix-percent-attrs-numeric)
|
(update :attrs #(reduce-kv fix-percent-attr-numeric % %))
|
||||||
|
|
||||||
(not= "objectBoundingBox" units)
|
(not= "objectBoundingBox" units)
|
||||||
(update :attrs fix-percent-attrs-viewbox))))]
|
(update :attrs fix-percent-attrs-viewbox))))]
|
||||||
|
|
||||||
(->> svg-data (map-nodes fix-percent-values)))))
|
(map-nodes fix-percent-values svg-data))))
|
||||||
|
|
||||||
(defn collect-images [svg-data]
|
(defn collect-images [svg-data]
|
||||||
(let [redfn (fn [acc {:keys [tag attrs]}]
|
(let [redfn (fn [acc {:keys [tag attrs]}]
|
||||||
|
|
|
@ -193,7 +193,8 @@
|
||||||
(defn create-group
|
(defn create-group
|
||||||
[name frame-id {:keys [x y width height offset-x offset-y] :as svg-data} {:keys [attrs]}]
|
[name frame-id {:keys [x y width height offset-x offset-y] :as svg-data} {:keys [attrs]}]
|
||||||
(let [transform (csvg/parse-transform (:transform attrs))
|
(let [transform (csvg/parse-transform (:transform attrs))
|
||||||
attrs (-> (d/without-keys attrs csvg/inheritable-props)
|
attrs (-> attrs
|
||||||
|
(d/without-keys csvg/inheritable-props)
|
||||||
(csvg/attrs->props))
|
(csvg/attrs->props))
|
||||||
vbox (grc/make-rect offset-x offset-y width height)]
|
vbox (grc/make-rect offset-x offset-y width height)]
|
||||||
(cts/setup-shape
|
(cts/setup-shape
|
||||||
|
@ -304,6 +305,8 @@
|
||||||
|
|
||||||
rx (d/nilv r rx)
|
rx (d/nilv r rx)
|
||||||
ry (d/nilv r ry)
|
ry (d/nilv r ry)
|
||||||
|
rx (d/nilv rx 0)
|
||||||
|
ry (d/nilv ry 0)
|
||||||
|
|
||||||
;; There are some svg circles in the internet that does not
|
;; There are some svg circles in the internet that does not
|
||||||
;; have cx and cy attrs, so we default them to 0
|
;; have cx and cy attrs, so we default them to 0
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
|
|
||||||
(defn pages-seq
|
(defn pages-seq
|
||||||
[fdata]
|
[fdata]
|
||||||
(vals (:pages-index fdata)))
|
(-> fdata :pages-index vals seq))
|
||||||
|
|
||||||
(defn update-page
|
(defn update-page
|
||||||
[file-data page-id f]
|
[file-data page-id f]
|
||||||
|
|
|
@ -79,25 +79,6 @@
|
||||||
(def text-align-types
|
(def text-align-types
|
||||||
#{"left" "right" "center" "justify"})
|
#{"left" "right" "center" "justify"})
|
||||||
|
|
||||||
(sm/define! ::selrect
|
|
||||||
[:and
|
|
||||||
{:title "Selrect"
|
|
||||||
:gen/gen (->> (sg/tuple (sg/small-double)
|
|
||||||
(sg/small-double)
|
|
||||||
(sg/small-double)
|
|
||||||
(sg/small-double))
|
|
||||||
(sg/fmap #(apply grc/make-rect %)))}
|
|
||||||
[:fn grc/rect?]
|
|
||||||
[:map
|
|
||||||
[:x ::sm/safe-number]
|
|
||||||
[:y ::sm/safe-number]
|
|
||||||
[:x1 ::sm/safe-number]
|
|
||||||
[:x2 ::sm/safe-number]
|
|
||||||
[:y1 ::sm/safe-number]
|
|
||||||
[:y2 ::sm/safe-number]
|
|
||||||
[:width ::sm/safe-number]
|
|
||||||
[:height ::sm/safe-number]]])
|
|
||||||
|
|
||||||
(sm/define! ::points
|
(sm/define! ::points
|
||||||
[:vector {:gen/max 4 :gen/min 4} ::gpt/point])
|
[:vector {:gen/max 4 :gen/min 4} ::gpt/point])
|
||||||
|
|
||||||
|
@ -133,7 +114,7 @@
|
||||||
[:id ::sm/uuid]
|
[:id ::sm/uuid]
|
||||||
[:name :string]
|
[:name :string]
|
||||||
[:type [::sm/one-of shape-types]]
|
[:type [::sm/one-of shape-types]]
|
||||||
[:selrect ::selrect]
|
[:selrect ::grc/rect]
|
||||||
[:points ::points]
|
[:points ::points]
|
||||||
[:transform ::gmt/matrix]
|
[:transform ::gmt/matrix]
|
||||||
[:transform-inverse ::gmt/matrix]
|
[:transform-inverse ::gmt/matrix]
|
||||||
|
@ -156,7 +137,7 @@
|
||||||
[:main-instance {:optional true} :boolean]
|
[:main-instance {:optional true} :boolean]
|
||||||
[:remote-synced {:optional true} :boolean]
|
[:remote-synced {:optional true} :boolean]
|
||||||
[:shape-ref {:optional true} ::sm/uuid]
|
[:shape-ref {:optional true} ::sm/uuid]
|
||||||
[:selrect {:optional true} ::selrect]
|
[:selrect {:optional true} ::grc/rect]
|
||||||
[:points {:optional true} ::points]
|
[:points {:optional true} ::points]
|
||||||
[:blocked {:optional true} :boolean]
|
[:blocked {:optional true} :boolean]
|
||||||
[:collapsed {:optional true} :boolean]
|
[:collapsed {:optional true} :boolean]
|
||||||
|
@ -430,7 +411,7 @@
|
||||||
:name "Path"
|
:name "Path"
|
||||||
:fills []
|
:fills []
|
||||||
:strokes [{:stroke-style :solid
|
:strokes [{:stroke-style :solid
|
||||||
:stroke-alignment :center
|
:stroke-alignment :inner
|
||||||
:stroke-width 2
|
:stroke-width 2
|
||||||
:stroke-color clr/black
|
:stroke-color clr/black
|
||||||
:stroke-opacity 1}]})
|
:stroke-opacity 1}]})
|
||||||
|
|
|
@ -563,7 +563,7 @@
|
||||||
padding: $s-12;
|
padding: $s-12;
|
||||||
border-radius: $br-8;
|
border-radius: $br-8;
|
||||||
z-index: $z-index-10;
|
z-index: $z-index-10;
|
||||||
color: var(--color-foreground-primary);
|
color: var(--modal-title-foreground-color);
|
||||||
background-color: var(--modal-background-color);
|
background-color: var(--modal-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -575,7 +575,7 @@
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
z-index: $z-index-modal;
|
z-index: $z-index-modal;
|
||||||
background-color: var(--color-background-subtle);
|
background-color: var(--overlay-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-container-base {
|
.modal-container-base {
|
||||||
|
|
|
@ -222,6 +222,7 @@
|
||||||
--assets-item-background-color: var(--color-background-tertiary);
|
--assets-item-background-color: var(--color-background-tertiary);
|
||||||
--assets-item-background-color-hover: var(--color-background-quaternary);
|
--assets-item-background-color-hover: var(--color-background-quaternary);
|
||||||
--assets-item-name-background-color: var(--db-secondary-80); // TODO: penpot file has a non-existing token
|
--assets-item-name-background-color: var(--db-secondary-80); // TODO: penpot file has a non-existing token
|
||||||
|
--assets-item-name-foreground-color-rest: var(--color-foreground-secondary);
|
||||||
--assets-item-name-foreground-color: var(--color-foreground-primary);
|
--assets-item-name-foreground-color: var(--color-foreground-primary);
|
||||||
--assets-item-name-foreground-color-hover: var(--color-foreground-primary);
|
--assets-item-name-foreground-color-hover: var(--color-foreground-primary);
|
||||||
--assets-item-name-foreground-color-disabled: var(--color-foreground-disabled);
|
--assets-item-name-foreground-color-disabled: var(--color-foreground-disabled);
|
||||||
|
|
|
@ -248,7 +248,7 @@
|
||||||
(assoc :stroke-style :solid)
|
(assoc :stroke-style :solid)
|
||||||
|
|
||||||
(not (contains? new-attrs :stroke-alignment))
|
(not (contains? new-attrs :stroke-alignment))
|
||||||
(assoc :stroke-alignment :center)
|
(assoc :stroke-alignment :inner)
|
||||||
|
|
||||||
:always
|
:always
|
||||||
(d/without-nils))]
|
(d/without-nils))]
|
||||||
|
|
|
@ -39,18 +39,7 @@
|
||||||
|
|
||||||
(def ^:private svgo-config
|
(def ^:private svgo-config
|
||||||
{:multipass false
|
{:multipass false
|
||||||
:plugins
|
:plugins ["safeAndFastPreset"]})
|
||||||
[{:name "safePreset"
|
|
||||||
:params {:overrides
|
|
||||||
{:convertColors
|
|
||||||
{:names2hex true
|
|
||||||
:shorthex false
|
|
||||||
:shortname false}
|
|
||||||
:convertTransform
|
|
||||||
{:matrixToTransform false
|
|
||||||
:convertToShorts false
|
|
||||||
:transformPrecision 4
|
|
||||||
:leadingZero false}}}}]})
|
|
||||||
|
|
||||||
(defn svg->clj
|
(defn svg->clj
|
||||||
[[name text]]
|
[[name text]]
|
||||||
|
|
|
@ -393,6 +393,9 @@
|
||||||
unames (volatile! (cfh/get-used-names (:objects page)))
|
unames (volatile! (cfh/get-used-names (:objects page)))
|
||||||
update-unames! (fn [new-name] (vswap! unames conj new-name))
|
update-unames! (fn [new-name] (vswap! unames conj new-name))
|
||||||
all-ids (reduce #(into %1 (cons %2 (cfh/get-children-ids all-objects %2))) (d/ordered-set) ids)
|
all-ids (reduce #(into %1 (cons %2 (cfh/get-children-ids all-objects %2))) (d/ordered-set) ids)
|
||||||
|
|
||||||
|
;; We need ids-map for remapping the grid layout. But when duplicating the guides
|
||||||
|
;; we calculate a new one because the components will have created new shapes.
|
||||||
ids-map (into {} (map #(vector % (uuid/next))) all-ids)
|
ids-map (into {} (map #(vector % (uuid/next))) all-ids)
|
||||||
|
|
||||||
changes
|
changes
|
||||||
|
@ -409,7 +412,15 @@
|
||||||
library-data
|
library-data
|
||||||
it
|
it
|
||||||
file-id)
|
file-id)
|
||||||
init-changes))]
|
init-changes))
|
||||||
|
|
||||||
|
;; We need to check the changes to get the ids-map
|
||||||
|
ids-map
|
||||||
|
(into {}
|
||||||
|
(comp
|
||||||
|
(filter #(= :add-obj (:type %)))
|
||||||
|
(map #(vector (:old-id %) (-> % :obj :id))))
|
||||||
|
(:redo-changes changes))]
|
||||||
|
|
||||||
(-> changes
|
(-> changes
|
||||||
(prepare-duplicate-flows shapes page ids-map)
|
(prepare-duplicate-flows shapes page ids-map)
|
||||||
|
@ -578,14 +589,16 @@
|
||||||
(defn- prepare-duplicate-guides
|
(defn- prepare-duplicate-guides
|
||||||
[changes shapes page ids-map delta]
|
[changes shapes page ids-map delta]
|
||||||
(let [guides (get-in page [:options :guides])
|
(let [guides (get-in page [:options :guides])
|
||||||
frames (->> shapes
|
frames (->> shapes (filter cfh/frame-shape?))
|
||||||
(filter #(= (:type %) :frame)))
|
|
||||||
new-guides (reduce
|
new-guides
|
||||||
|
(reduce
|
||||||
(fn [g frame]
|
(fn [g frame]
|
||||||
(let [new-id (ids-map (:id frame))
|
(let [new-id (ids-map (:id frame))
|
||||||
new-frame (-> frame
|
new-frame (-> frame (gsh/move delta))
|
||||||
(gsh/move delta))
|
|
||||||
new-guides (->> guides
|
new-guides
|
||||||
|
(->> guides
|
||||||
(vals)
|
(vals)
|
||||||
(filter #(= (:frame-id %) (:id frame)))
|
(filter #(= (:frame-id %) (:id frame)))
|
||||||
(map #(-> %
|
(map #(-> %
|
||||||
|
|
|
@ -72,13 +72,15 @@
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
(let [page-id (:current-page-id state)
|
(let [page-id (:current-page-id state)
|
||||||
objects (wsh/lookup-page-objects state page-id)
|
objects (wsh/lookup-page-objects state page-id)
|
||||||
shapes (->> shapes (remove #(dm/get-in objects [% :blocked])))
|
shapes (->> shapes
|
||||||
|
(remove #(dm/get-in objects [% :blocked]))
|
||||||
|
(cfh/order-by-indexed-shapes objects))
|
||||||
|
|
||||||
changes (-> (pcb/empty-changes it page-id)
|
changes (-> (pcb/empty-changes it page-id)
|
||||||
(pcb/with-objects objects))
|
(pcb/with-objects objects))
|
||||||
changes (cfsh/prepare-move-shapes-into-frame changes
|
|
||||||
frame-id
|
changes (cfsh/prepare-move-shapes-into-frame changes frame-id shapes objects)]
|
||||||
shapes
|
|
||||||
objects)]
|
|
||||||
(if (some? changes)
|
(if (some? changes)
|
||||||
(rx/of (dch/commit-changes changes))
|
(rx/of (dch/commit-changes changes))
|
||||||
(rx/empty))))))
|
(rx/empty))))))
|
||||||
|
@ -176,12 +178,16 @@
|
||||||
interactions)))
|
interactions)))
|
||||||
(vals objects))
|
(vals objects))
|
||||||
|
|
||||||
;; If any of the deleted shapes is a frame with guides
|
ids-set (set ids)
|
||||||
guides (into {}
|
guides-to-remove
|
||||||
(comp (map second)
|
(->> (dm/get-in page [:options :guides])
|
||||||
(remove #(contains? ids (:frame-id %)))
|
(vals)
|
||||||
(map (juxt :id identity)))
|
(filter #(contains? ids-set (:frame-id %)))
|
||||||
(dm/get-in page [:options :guides]))
|
(map :id))
|
||||||
|
|
||||||
|
guides
|
||||||
|
(->> guides-to-remove
|
||||||
|
(reduce dissoc (dm/get-in page [:options :guides])))
|
||||||
|
|
||||||
starting-flows
|
starting-flows
|
||||||
(filter (fn [flow]
|
(filter (fn [flow]
|
||||||
|
|
|
@ -58,6 +58,10 @@
|
||||||
;; We need to store the handle-blur ref so we can call it on unmount
|
;; We need to store the handle-blur ref so we can call it on unmount
|
||||||
dirty-ref (mf/use-ref false)
|
dirty-ref (mf/use-ref false)
|
||||||
|
|
||||||
|
;; Last value input by the user we need to store to save on unmount
|
||||||
|
|
||||||
|
last-value* (mf/use-var nil)
|
||||||
|
|
||||||
parse-value
|
parse-value
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps min-value max-value value nillable? default)
|
(mf/deps min-value max-value value nillable? default)
|
||||||
|
@ -102,7 +106,20 @@
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps wrap-value? min-value max-value parse-value apply-value)
|
(mf/deps wrap-value? min-value max-value parse-value apply-value)
|
||||||
(fn [event up? down?]
|
(fn [event up? down?]
|
||||||
(let [current-value (parse-value)]
|
(let [current-value (parse-value)
|
||||||
|
current-value
|
||||||
|
(cond
|
||||||
|
(and (not current-value) down? max-value)
|
||||||
|
max-value
|
||||||
|
|
||||||
|
(and (not current-value) up? min-value)
|
||||||
|
min-value
|
||||||
|
|
||||||
|
(not current-value)
|
||||||
|
(d/nilv default 0)
|
||||||
|
|
||||||
|
:else
|
||||||
|
current-value)]
|
||||||
(when current-value
|
(when current-value
|
||||||
(let [increment (cond
|
(let [increment (cond
|
||||||
(kbd/shift? event)
|
(kbd/shift? event)
|
||||||
|
@ -152,6 +169,13 @@
|
||||||
(update-input value-str)
|
(update-input value-str)
|
||||||
(dom/blur! node)))))
|
(dom/blur! node)))))
|
||||||
|
|
||||||
|
handle-key-up
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps parse-value)
|
||||||
|
(fn []
|
||||||
|
;; Store the last value inputed
|
||||||
|
(reset! last-value* (parse-value))))
|
||||||
|
|
||||||
handle-mouse-wheel
|
handle-mouse-wheel
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps set-delta)
|
(mf/deps set-delta)
|
||||||
|
@ -167,7 +191,7 @@
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps parse-value apply-value update-input on-blur)
|
(mf/deps parse-value apply-value update-input on-blur)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [new-value (or (parse-value) default)]
|
(let [new-value (or @last-value* default)]
|
||||||
(if (or nillable? new-value)
|
(if (or nillable? new-value)
|
||||||
(apply-value event new-value)
|
(apply-value event new-value)
|
||||||
(update-input new-value)))
|
(update-input new-value)))
|
||||||
|
@ -208,6 +232,7 @@
|
||||||
(obj/set! "defaultValue" (fmt/format-number value))
|
(obj/set! "defaultValue" (fmt/format-number value))
|
||||||
(obj/set! "title" title)
|
(obj/set! "title" title)
|
||||||
(obj/set! "onKeyDown" handle-key-down)
|
(obj/set! "onKeyDown" handle-key-down)
|
||||||
|
(obj/set! "onKeyUp" handle-key-up)
|
||||||
(obj/set! "onBlur" handle-blur)
|
(obj/set! "onBlur" handle-blur)
|
||||||
(obj/set! "onFocus" handle-focus))]
|
(obj/set! "onFocus" handle-focus))]
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
placeholder (unchecked-get props "placeholder")
|
placeholder (unchecked-get props "placeholder")
|
||||||
icon (unchecked-get props "icon")
|
icon (unchecked-get props "icon")
|
||||||
autofocus (unchecked-get props "auto-focus")
|
autofocus (unchecked-get props "auto-focus")
|
||||||
|
id (unchecked-get props "id")
|
||||||
|
|
||||||
|
|
||||||
handle-change
|
handle-change
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
@ -51,7 +53,8 @@
|
||||||
children
|
children
|
||||||
[:div {:class (stl/css :search-input-wrapper)}
|
[:div {:class (stl/css :search-input-wrapper)}
|
||||||
icon
|
icon
|
||||||
[:input {:on-change handle-change
|
[:input {:id id
|
||||||
|
:on-change handle-change
|
||||||
:value value
|
:value value
|
||||||
:auto-focus autofocus
|
:auto-focus autofocus
|
||||||
:placeholder placeholder
|
:placeholder placeholder
|
||||||
|
|
|
@ -71,8 +71,8 @@
|
||||||
[:rect.margin-rect
|
[:rect.margin-rect
|
||||||
{:x (:x rect-data)
|
{:x (:x rect-data)
|
||||||
:y (:y rect-data)
|
:y (:y rect-data)
|
||||||
:width (:width rect-data)
|
:width (max 0 (:width rect-data))
|
||||||
:height (:height rect-data)
|
:height (max 0 (:height rect-data))
|
||||||
:on-pointer-enter on-pointer-enter
|
:on-pointer-enter on-pointer-enter
|
||||||
:on-pointer-leave on-pointer-leave
|
:on-pointer-leave on-pointer-leave
|
||||||
:on-pointer-down on-pointer-down
|
:on-pointer-down on-pointer-down
|
||||||
|
|
|
@ -32,8 +32,7 @@
|
||||||
:selected (= editing-stop offset))
|
:selected (= editing-stop offset))
|
||||||
:data-value (str offset)
|
:data-value (str offset)
|
||||||
:on-click on-select-stop
|
:on-click on-select-stop
|
||||||
:style {:left (dm/str (* offset 100) "%")
|
:style {:left (dm/str (* offset 100) "%")}
|
||||||
:backgroundColor hex}
|
|
||||||
:key (dm/str offset)}
|
:key (dm/str offset)}
|
||||||
|
|
||||||
[:div {:class (stl/css :gradient-stop-color)
|
[:div {:class (stl/css :gradient-stop-color)
|
||||||
|
|
|
@ -32,22 +32,31 @@
|
||||||
|
|
||||||
.gradient-stop-wrapper {
|
.gradient-stop-wrapper {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
width: calc(100% - 2rem);
|
width: calc(100% - $s-40);
|
||||||
|
left: $s-20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gradient-stop {
|
.gradient-stop {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 50% 50%;
|
grid-template-columns: 50% 50%;
|
||||||
|
padding: 0;
|
||||||
width: $s-16;
|
width: $s-16;
|
||||||
height: $s-24;
|
height: $s-24;
|
||||||
border-radius: $br-4;
|
border-radius: $br-4;
|
||||||
margin-top: calc(-1 * $s-2);
|
margin-top: calc(-1 * $s-2);
|
||||||
margin-left: calc(-1 * $s-8);
|
margin-left: calc(-1 * $s-8);
|
||||||
border: $s-2 solid var(--colorpicker-handlers-color);
|
border: $s-2 solid var(--colorpicker-handlers-color);
|
||||||
background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=")
|
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=");
|
||||||
left center;
|
background-position: left center;
|
||||||
|
background-size: 8px;
|
||||||
&.selected {
|
&.selected {
|
||||||
border: $s-2 solid var(--colorpicker-details-color-selected);
|
border: $s-2 solid var(--colorpicker-details-color-selected);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.gradient-stop-color,
|
||||||
|
.gradient-stop-alpha {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
|
@ -176,20 +176,6 @@
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listing-option-btn {
|
|
||||||
@include flexCenter;
|
|
||||||
cursor: pointer;
|
|
||||||
background-color: var(--button-radio-background-color-rest);
|
|
||||||
|
|
||||||
&.first {
|
|
||||||
margin-left: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
svg {
|
|
||||||
@extend .button-icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.add-component {
|
.add-component {
|
||||||
@extend .button-tertiary;
|
@extend .button-tertiary;
|
||||||
height: $s-32;
|
height: $s-32;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
(ns app.main.ui.workspace.sidebar.options.menus.component
|
(ns app.main.ui.workspace.sidebar.options.menus.component
|
||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.types.component :as ctk]
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.file :as ctf]
|
[app.common.types.file :as ctf]
|
||||||
|
@ -28,6 +29,7 @@
|
||||||
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
[app.main.ui.workspace.sidebar.assets.common :as cmm]
|
||||||
[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.timers :as tm]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
@ -197,18 +199,21 @@
|
||||||
(mf/defc component-group-item
|
(mf/defc component-group-item
|
||||||
[{:keys [item on-enter-group] :as props}]
|
[{:keys [item on-enter-group] :as props}]
|
||||||
(let [group-name (:name item)
|
(let [group-name (:name item)
|
||||||
path (cfh/butlast-path group-name)
|
path (cfh/butlast-path-with-dots group-name)
|
||||||
on-group-click #(on-enter-group group-name)]
|
on-group-click #(on-enter-group group-name)]
|
||||||
[:div {:class (stl/css :component-group)
|
[:div {:class (stl/css :component-group)
|
||||||
:key (uuid/next) :on-click on-group-click
|
:key (uuid/next) :on-click on-group-click
|
||||||
:title group-name}
|
:title group-name}
|
||||||
[:div
|
|
||||||
|
[:div {:class (stl/css :path-wrapper)}
|
||||||
(when-not (str/blank? path)
|
(when-not (str/blank? path)
|
||||||
[:span {:class (stl/css :component-group-path)}
|
[:span {:class (stl/css :component-group-path)}
|
||||||
(str "\u00A0/\u00A0" path)])
|
(str "\u00A0\u2022\u00A0" path)])
|
||||||
[:span {:class (stl/css :component-group-name)}
|
[:span {:class (stl/css :component-group-name)}
|
||||||
(cfh/last-path group-name)]]
|
(cfh/last-path group-name)]]
|
||||||
[:span i/arrow-refactor]]))
|
|
||||||
|
[:span {:class (stl/css :arrow-icon)}
|
||||||
|
i/arrow-refactor]]))
|
||||||
|
|
||||||
(mf/defc component-swap
|
(mf/defc component-swap
|
||||||
[{:keys [shapes] :as props}]
|
[{:keys [shapes] :as props}]
|
||||||
|
@ -228,7 +233,9 @@
|
||||||
file-id (if every-same-file?
|
file-id (if every-same-file?
|
||||||
(:component-file shape)
|
(:component-file shape)
|
||||||
current-file-id)
|
current-file-id)
|
||||||
|
|
||||||
orig-components (map #(ctf/get-component libraries (:component-file %) (:component-id %)) shapes)
|
orig-components (map #(ctf/get-component libraries (:component-file %) (:component-id %)) shapes)
|
||||||
|
|
||||||
paths (->> orig-components
|
paths (->> orig-components
|
||||||
(map :path)
|
(map :path)
|
||||||
(map cfh/split-path))
|
(map cfh/split-path))
|
||||||
|
@ -245,6 +252,7 @@
|
||||||
(cfh/join-path (if (not every-same-file?)
|
(cfh/join-path (if (not every-same-file?)
|
||||||
""
|
""
|
||||||
(find-common-path [] 0))))
|
(find-common-path [] 0))))
|
||||||
|
|
||||||
filters* (mf/use-state
|
filters* (mf/use-state
|
||||||
{:term ""
|
{:term ""
|
||||||
:file-id file-id
|
:file-id file-id
|
||||||
|
@ -252,7 +260,9 @@
|
||||||
:listing-thumbs? false})
|
:listing-thumbs? false})
|
||||||
|
|
||||||
filters (deref filters*)
|
filters (deref filters*)
|
||||||
|
|
||||||
is-search? (not (str/blank? (:term filters)))
|
is-search? (not (str/blank? (:term filters)))
|
||||||
|
|
||||||
current-library-id (if (contains? libraries (:file-id filters))
|
current-library-id (if (contains? libraries (:file-id filters))
|
||||||
(:file-id filters)
|
(:file-id filters)
|
||||||
current-file-id)
|
current-file-id)
|
||||||
|
@ -264,7 +274,7 @@
|
||||||
components (->> (get-in libraries [current-library-id :data :components])
|
components (->> (get-in libraries [current-library-id :data :components])
|
||||||
vals
|
vals
|
||||||
(remove #(true? (:deleted %)))
|
(remove #(true? (:deleted %)))
|
||||||
(map #(assoc % :full-name (cfh/merge-path-item (:path %) (:name %)))))
|
(map #(assoc % :full-name (cfh/merge-path-item-with-dot (:path %) (:name %)))))
|
||||||
|
|
||||||
get-subgroups (fn [path]
|
get-subgroups (fn [path]
|
||||||
(let [split-path (cfh/split-path path)]
|
(let [split-path (cfh/split-path path)]
|
||||||
|
@ -335,25 +345,33 @@
|
||||||
toggle-list-style
|
toggle-list-style
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [style]
|
(fn [style]
|
||||||
(swap! filters* assoc :listing-thumbs? (= style "grid"))))]
|
(swap! filters* assoc :listing-thumbs? (= style "grid"))))
|
||||||
|
|
||||||
|
filters-but-last (cfh/butlast-path (:path filters))
|
||||||
|
last-filters (cfh/last-path (:path filters))
|
||||||
|
filter-path-with-dots (->> filters-but-last (cfh/split-path) (cfh/join-path-with-dot))]
|
||||||
|
|
||||||
[:div {:class (stl/css :component-swap)}
|
[:div {:class (stl/css :component-swap)}
|
||||||
[:div {:class (stl/css :element-set-title)}
|
[:div {:class (stl/css :element-set-title)}
|
||||||
[:span (tr "workspace.options.component.swap")]]
|
[:span (tr "workspace.options.component.swap")]]
|
||||||
[:div {:class (stl/css :component-swap-content)}
|
[:div {:class (stl/css :component-swap-content)}
|
||||||
|
[:div {:class (stl/css :fields-wrapper)}
|
||||||
[:div {:class (stl/css :search-field)}
|
[:div {:class (stl/css :search-field)}
|
||||||
[:& search-bar {:on-change on-search-term-change
|
[:& search-bar {:on-change on-search-term-change
|
||||||
:clear-action on-search-clear-click
|
:clear-action on-search-clear-click
|
||||||
|
:class (stl/css :search-wrapper)
|
||||||
|
:id "swap-component-search-filter"
|
||||||
:value (:term filters)
|
:value (:term filters)
|
||||||
:placeholder (str (tr "labels.search") " " (get-in libraries [current-library-id :name]))
|
:placeholder (str (tr "labels.search") " " (get-in libraries [current-library-id :name]))
|
||||||
:icon (mf/html [:span {:class (stl/css :search-icon)} i/search-refactor])}]]
|
:icon (mf/html [:span {:class (stl/css :search-icon)} i/search-refactor])}]]
|
||||||
|
|
||||||
[:div {:class (stl/css :select-field)}
|
|
||||||
[:& select {:class (stl/css :select-library)
|
[:& select {:class (stl/css :select-library)
|
||||||
:default-value current-library-id
|
:default-value current-library-id
|
||||||
:options libraries-options
|
:options libraries-options
|
||||||
:on-change on-library-change}]]
|
:on-change on-library-change}]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :swap-wrapper)}
|
||||||
|
[:div {:class (stl/css :library-name-wrapper)}
|
||||||
[:div {:class (stl/css :library-name)} current-library-name]
|
[:div {:class (stl/css :library-name)} current-library-name]
|
||||||
|
|
||||||
[:div {:class (stl/css :listing-options-wrapper)}
|
[:div {:class (stl/css :listing-options-wrapper)}
|
||||||
|
@ -362,26 +380,25 @@
|
||||||
:on-change toggle-list-style
|
:on-change toggle-list-style
|
||||||
:name "swap-listing-style"}
|
:name "swap-listing-style"}
|
||||||
[:& radio-button {:icon i/view-as-list-refactor
|
[:& radio-button {:icon i/view-as-list-refactor
|
||||||
:icon-class (stl/css :radio-button)
|
|
||||||
:value "list"
|
:value "list"
|
||||||
:id "swap-opt-list"}]
|
:id "swap-opt-list"}]
|
||||||
[:& radio-button {:icon i/flex-grid-refactor
|
[:& radio-button {:icon i/flex-grid-refactor
|
||||||
:icon-class (stl/css :radio-button)
|
|
||||||
:value "grid"
|
:value "grid"
|
||||||
:id "swap-opt-grid"}]]]
|
:id "swap-opt-grid"}]]]]
|
||||||
|
|
||||||
|
(when-not (or is-search? (str/empty? (:path filters)))
|
||||||
(if (or is-search? (str/empty? (:path filters)))
|
|
||||||
[:div {:class (stl/css :component-path-empty)}]
|
|
||||||
[:button {:class (stl/css :component-path)
|
[:button {:class (stl/css :component-path)
|
||||||
:on-click on-go-back
|
:on-click on-go-back
|
||||||
:title (:path filters)}
|
:title filter-path-with-dots}
|
||||||
[:span i/arrow-refactor]
|
[:span {:class (stl/css :back-arrow)} i/arrow-refactor]
|
||||||
[:span (:path filters)]])
|
(when-not (= "" filter-path-with-dots)
|
||||||
|
[:span {:class (stl/css :path-name)}
|
||||||
|
(dm/str "\u00A0\u2022\u00A0" filter-path-with-dots)])
|
||||||
|
[:span {:class (stl/css :path-name-last)} last-filters]])
|
||||||
|
|
||||||
(when (empty? items)
|
(when (empty? items)
|
||||||
[:div {:class (stl/css :component-list-empty)}
|
[:div {:class (stl/css :component-list-empty)}
|
||||||
(tr "workspace.options.component.swap.empty")])
|
(tr "workspace.options.component.swap.empty")]) ;;TODO review this empty space
|
||||||
|
|
||||||
(when (:listing-thumbs? filters)
|
(when (:listing-thumbs? filters)
|
||||||
[:div {:class (stl/css :component-list)}
|
[:div {:class (stl/css :component-list)}
|
||||||
|
@ -407,17 +424,20 @@
|
||||||
:component-id current-comp-id
|
:component-id current-comp-id
|
||||||
:is-search is-search?
|
:is-search is-search?
|
||||||
:listing-thumbs (:listing-thumbs? filters)}])
|
:listing-thumbs (:listing-thumbs? filters)}])
|
||||||
[:& component-group-item {:item item :on-enter-group on-enter-group}]))]]]))
|
[:& component-group-item {:item item
|
||||||
|
:key (:id item)
|
||||||
|
:on-enter-group on-enter-group}]))]]]]))
|
||||||
|
|
||||||
(mf/defc component-ctx-menu
|
(mf/defc component-ctx-menu
|
||||||
[{:keys [menu-entries on-close show] :as props}]
|
[{:keys [menu-entries on-close show main-instance] :as props}]
|
||||||
(let [do-action
|
(let [do-action
|
||||||
(fn [action event]
|
(fn [action event]
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(action)
|
(action)
|
||||||
(on-close))]
|
(on-close))]
|
||||||
[:& dropdown {:show show :on-close on-close}
|
[:& dropdown {:show show :on-close on-close}
|
||||||
[:ul {:class (stl/css :custom-select-dropdown)}
|
[:ul {:class (stl/css-case :custom-select-dropdown true
|
||||||
|
:not-main (not main-instance))}
|
||||||
(for [entry menu-entries :when (not (nil? entry))]
|
(for [entry menu-entries :when (not (nil? entry))]
|
||||||
[:li {:key (uuid/next)
|
[:li {:key (uuid/next)
|
||||||
:class (stl/css :dropdown-element)
|
:class (stl/css :dropdown-element)
|
||||||
|
@ -471,10 +491,14 @@
|
||||||
open-component-panel
|
open-component-panel
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps can-swap? shapes)
|
(mf/deps can-swap? shapes)
|
||||||
#(when can-swap? (st/emit! (dwsp/open-specialized-panel :component-swap))))
|
(fn []
|
||||||
|
(let [search-id "swap-component-search-filter"]
|
||||||
|
(when can-swap? (st/emit! (dwsp/open-specialized-panel :component-swap)))
|
||||||
|
(tm/schedule-on-idle #(dom/focus! (dom/get-element search-id))))))
|
||||||
|
|
||||||
menu-entries (cmm/generate-components-menu-entries shapes components-v2)
|
menu-entries (cmm/generate-components-menu-entries shapes components-v2)
|
||||||
show-menu? (seq menu-entries)]
|
show-menu? (seq menu-entries)
|
||||||
|
path (->> component (:path) (cfh/split-path) (cfh/join-path-with-dot))]
|
||||||
|
|
||||||
(when (seq shapes)
|
(when (seq shapes)
|
||||||
[:div {:class (stl/css :element-set)}
|
[:div {:class (stl/css :element-set)}
|
||||||
|
@ -482,8 +506,9 @@
|
||||||
(if swap-opened?
|
(if swap-opened?
|
||||||
[:button {:class (stl/css :title-back)
|
[:button {:class (stl/css :title-back)
|
||||||
:on-click on-component-back}
|
:on-click on-component-back}
|
||||||
[:span i/arrow-refactor]
|
[:span {:class (stl/css :icon-back)} i/arrow-refactor]
|
||||||
[:span (tr "workspace.options.component")]]
|
[:span (tr "workspace.options.component")]]
|
||||||
|
|
||||||
[:& title-bar {:collapsable true
|
[:& title-bar {:collapsable true
|
||||||
:collapsed (not open?)
|
:collapsed (not open?)
|
||||||
:on-collapsed toggle-content
|
:on-collapsed toggle-content
|
||||||
|
@ -496,31 +521,40 @@
|
||||||
|
|
||||||
(when open?
|
(when open?
|
||||||
[:div {:class (stl/css :element-content)}
|
[:div {:class (stl/css :element-content)}
|
||||||
[:div {:class (stl/css :component-wrapper)}
|
[:div {:class (stl/css-case :component-wrapper true
|
||||||
[:div {:class (stl/css-case :component-name-wrapper true
|
:with-actions show-menu?)}
|
||||||
|
[:button {:class (stl/css-case :component-name-wrapper true
|
||||||
:with-main (and can-swap? (not multi))
|
:with-main (and can-swap? (not multi))
|
||||||
:swappeable (and can-swap? (not swap-opened?)))
|
:swappeable (and can-swap? (not swap-opened?)))
|
||||||
:on-click open-component-panel}
|
:on-click open-component-panel}
|
||||||
|
|
||||||
[:span {:class (stl/css :component-icon)}
|
[:span {:class (stl/css :component-icon)}
|
||||||
(if main-instance?
|
(if main-instance?
|
||||||
i/component-refactor
|
i/component-refactor
|
||||||
i/copy-refactor)]
|
i/copy-refactor)]
|
||||||
|
|
||||||
[:div {:class (stl/css :component-name)} (if multi
|
[:div {:class (stl/css :name-wrapper)}
|
||||||
|
[:div {:class (stl/css :component-name)}
|
||||||
|
(if multi
|
||||||
(tr "settings.multiple")
|
(tr "settings.multiple")
|
||||||
(cfh/last-path shape-name))]
|
(cfh/last-path shape-name))]
|
||||||
|
|
||||||
|
(when (and can-swap? (not multi))
|
||||||
|
[:div {:class (stl/css :component-parent-name)}
|
||||||
|
(cfh/merge-path-item-with-dot path (:name component))])]]
|
||||||
|
|
||||||
(when show-menu?
|
(when show-menu?
|
||||||
[:div {:class (stl/css :component-actions)}
|
[:div {:class (stl/css :component-actions)}
|
||||||
[:button {:class (stl/css :menu-btn)
|
[:button {:class (stl/css-case :menu-btn true
|
||||||
|
:selected menu-open?)
|
||||||
:on-click on-menu-click}
|
:on-click on-menu-click}
|
||||||
i/menu-refactor]
|
i/menu-refactor]
|
||||||
|
|
||||||
[:& component-ctx-menu {:show menu-open?
|
[:& component-ctx-menu {:show menu-open?
|
||||||
:on-close on-menu-close
|
:on-close on-menu-close
|
||||||
:menu-entries menu-entries}]])
|
:menu-entries menu-entries
|
||||||
(when (and can-swap? (not multi))
|
:main-instance main-instance?}]])]
|
||||||
[:div {:class (stl/css :component-parent-name)}
|
|
||||||
(cfh/merge-path-item (:path component) (:name component))])]]
|
|
||||||
(when swap-opened?
|
(when swap-opened?
|
||||||
[:& component-swap {:shapes copies}])
|
[:& component-swap {:shapes copies}])
|
||||||
|
|
||||||
|
|
|
@ -7,33 +7,132 @@
|
||||||
@import "refactor/common-refactor.scss";
|
@import "refactor/common-refactor.scss";
|
||||||
.element-set {
|
.element-set {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
padding-top: $s-8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-content {
|
.element-content {
|
||||||
@include flexColumn;
|
@include flexColumn;
|
||||||
margin-bottom: $s-8;
|
}
|
||||||
|
|
||||||
|
.title-back {
|
||||||
|
@include tabTitleTipography;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: $s-4;
|
||||||
|
width: 100%;
|
||||||
|
height: $s-32;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
border-radius: $br-8;
|
||||||
|
background-color: var(--title-background-color);
|
||||||
|
color: var(--title-foreground-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-back {
|
||||||
|
@include flexCenter;
|
||||||
|
width: $s-12;
|
||||||
|
height: 100%;
|
||||||
|
svg {
|
||||||
|
height: $s-12;
|
||||||
|
width: $s-12;
|
||||||
|
stroke: var(--icon-foreground);
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-wrapper {
|
.component-wrapper {
|
||||||
display: flex;
|
width: 100%;
|
||||||
margin: 0 $s-4 0 $s-8;
|
min-height: $s-32;
|
||||||
|
border-radius: $br-8;
|
||||||
|
|
||||||
|
&.with-actions {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr $s-28;
|
||||||
|
gap: $s-2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-name-wrapper {
|
.component-name-wrapper {
|
||||||
@extend .asset-element;
|
@include buttonStyle;
|
||||||
@include flexRow;
|
cursor: default;
|
||||||
flex-grow: 1;
|
display: grid;
|
||||||
height: 100%;
|
grid-template-columns: $s-12 1fr;
|
||||||
width: 100%;
|
gap: $s-4;
|
||||||
flex-wrap: wrap;
|
padding: 0 $s-8;
|
||||||
padding: 0 0 0 $s-12;
|
border-radius: $br-8 0 0 $br-8;
|
||||||
margin-top: $s-8;
|
background-color: var(--assets-item-background-color);
|
||||||
|
color: var(--assets-item-name-foreground-color-hover);
|
||||||
&.with-main {
|
&:hover {
|
||||||
padding-bottom: $s-12;
|
background-color: var(--assets-item-background-color-hover);
|
||||||
|
color: var(--assets-item-name-foreground-color-hover);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.component-icon {
|
||||||
|
@include flexCenter;
|
||||||
|
height: $s-32;
|
||||||
|
width: $s-12;
|
||||||
|
svg {
|
||||||
|
@extend .button-icon-small;
|
||||||
|
stroke: var(--icon-foreground);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name-wrapper {
|
||||||
|
@include flexColumn;
|
||||||
|
min-height: $s-32;
|
||||||
|
padding: $s-8 0 $s-8 $s-2;
|
||||||
|
border-radius: $br-8 0 0 $br-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-name {
|
||||||
|
@include titleTipography;
|
||||||
|
@include textEllipsis;
|
||||||
|
direction: rtl;
|
||||||
|
text-align: left;
|
||||||
|
min-height: $s-16;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-parent-name {
|
||||||
|
@include titleTipography;
|
||||||
|
@include textEllipsis;
|
||||||
|
direction: rtl;
|
||||||
|
text-align: left;
|
||||||
|
min-height: $s-16;
|
||||||
|
max-width: $s-184;
|
||||||
|
color: var(--title-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-actions {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-btn {
|
||||||
|
@extend .button-tertiary;
|
||||||
|
height: 100%;
|
||||||
|
width: $s-28;
|
||||||
|
border-radius: 0 $br-8 $br-8 0;
|
||||||
|
background-color: var(--assets-item-background-color);
|
||||||
|
color: var(--assets-item-name-foreground-color-hover);
|
||||||
|
svg {
|
||||||
|
@extend .button-icon;
|
||||||
|
min-height: $s-16;
|
||||||
|
min-width: $s-16;
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
background-color: var(--assets-item-background-color-hover);
|
||||||
|
color: var(--assets-item-name-foreground-color-hover);
|
||||||
|
&.selected {
|
||||||
|
@extend .button-icon-selected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-btn.selected {
|
||||||
|
@extend .button-icon-selected;
|
||||||
|
}
|
||||||
|
|
||||||
.copy-text {
|
.copy-text {
|
||||||
@include titleTipography;
|
@include titleTipography;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
@ -43,52 +142,10 @@
|
||||||
margin-right: $s-8;
|
margin-right: $s-8;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-icon {
|
|
||||||
@include flexCenter;
|
|
||||||
height: $s-24;
|
|
||||||
width: $s-24;
|
|
||||||
svg {
|
|
||||||
@extend .button-icon;
|
|
||||||
stroke: var(--icon-foreground);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-name {
|
|
||||||
@include titleTipography;
|
|
||||||
@include textEllipsis;
|
|
||||||
direction: rtl;
|
|
||||||
text-align: left;
|
|
||||||
width: 70%;
|
|
||||||
flex-grow: 2;
|
|
||||||
margin-left: $s-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-parent-name {
|
|
||||||
@include titleTipography;
|
|
||||||
@include textEllipsis;
|
|
||||||
text-align: left;
|
|
||||||
max-width: 95%;
|
|
||||||
padding-left: $s-36;
|
|
||||||
color: var(--title-foreground-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.swappeable {
|
.swappeable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-actions {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.menu-btn {
|
|
||||||
@extend .button-tertiary;
|
|
||||||
height: $s-32;
|
|
||||||
width: $s-28;
|
|
||||||
svg {
|
|
||||||
@extend .button-icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.custom-select-dropdown {
|
.custom-select-dropdown {
|
||||||
@extend .dropdown-wrapper;
|
@extend .dropdown-wrapper;
|
||||||
right: 0;
|
right: 0;
|
||||||
|
@ -96,44 +153,14 @@
|
||||||
width: $s-252;
|
width: $s-252;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.not-main {
|
||||||
|
top: $s-56;
|
||||||
|
}
|
||||||
|
|
||||||
.dropdown-element {
|
.dropdown-element {
|
||||||
@extend .dropdown-element-base;
|
@extend .dropdown-element-base;
|
||||||
}
|
}
|
||||||
|
|
||||||
.title-back {
|
|
||||||
@include tabTitleTipography;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
|
||||||
background-color: var(--title-background-color);
|
|
||||||
color: var(--title-foreground-color);
|
|
||||||
text-align: left;
|
|
||||||
border: 0;
|
|
||||||
margin-bottom: $s-16;
|
|
||||||
svg {
|
|
||||||
height: $s-8;
|
|
||||||
width: $s-8;
|
|
||||||
stroke: var(--icon-foreground);
|
|
||||||
margin-right: $s-16;
|
|
||||||
transform: rotate(180deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-field {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: $s-32;
|
|
||||||
margin: $s-16 $s-4 $s-4 $s-12;
|
|
||||||
border-radius: $br-8;
|
|
||||||
font-family: "worksans", sans-serif;
|
|
||||||
background-color: var(--input-background-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-box {
|
|
||||||
align-items: center;
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-wrapper {
|
.icon-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
svg {
|
svg {
|
||||||
|
@ -175,52 +202,52 @@
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
@include flexCenter;
|
@include flexCenter;
|
||||||
width: $s-28;
|
width: $s-12;
|
||||||
|
margin-left: $s-8;
|
||||||
svg {
|
svg {
|
||||||
@extend .button-icon-small;
|
@extend .button-icon-small;
|
||||||
stroke: var(--icon-foreground);
|
stroke: var(--icon-foreground);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.select-field {
|
|
||||||
margin: $s-8 $s-4 0 $s-12;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select-library {
|
|
||||||
padding-left: $s-20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listing-options-wrapper {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listing-options {
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: $s-4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-path {
|
.component-path {
|
||||||
@include titleTipography;
|
display: flex;
|
||||||
@include textEllipsis;
|
align-items: center;
|
||||||
text-align: left;
|
gap: $s-4;
|
||||||
cursor: pointer;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: $s-32;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
background-color: var(--title-background-color);
|
background-color: var(--title-background-color);
|
||||||
color: var(--title-foreground-color);
|
color: var(--title-foreground-color);
|
||||||
border: 0;
|
cursor: pointer;
|
||||||
margin: $s-16 0 $s-12 0;
|
}
|
||||||
padding: 0 $s-16 0 $s-24;
|
|
||||||
|
.back-arrow {
|
||||||
|
@include flexCenter;
|
||||||
|
height: $s-32;
|
||||||
svg {
|
svg {
|
||||||
height: $s-8;
|
height: $s-12;
|
||||||
width: $s-8;
|
width: $s-12;
|
||||||
stroke: var(--icon-foreground);
|
stroke: var(--icon-foreground);
|
||||||
margin-right: $s-16;
|
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-path-empty {
|
.path-name {
|
||||||
height: $s-16;
|
@include titleTipography;
|
||||||
|
@include textEllipsis;
|
||||||
|
direction: rtl;
|
||||||
|
height: $s-32;
|
||||||
|
padding: $s-8 0 $s-8 $s-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.path-name-last {
|
||||||
|
@include titleTipography;
|
||||||
|
@include textEllipsis;
|
||||||
|
height: $s-32;
|
||||||
|
padding: $s-8 0 $s-8 $s-2;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-list-empty {
|
.component-list-empty {
|
||||||
|
@ -229,10 +256,6 @@
|
||||||
color: $df-secondary;
|
color: $df-secondary;
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-list {
|
|
||||||
margin: 0 $s-4 0 $s-8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-item {
|
.component-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -280,40 +303,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.component-group {
|
|
||||||
@include titleTipography;
|
|
||||||
text-align: left;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0 $s-16 $s-8 $s-8;
|
|
||||||
justify-content: space-between;
|
|
||||||
cursor: pointer;
|
|
||||||
height: $s-24;
|
|
||||||
svg {
|
|
||||||
height: $s-8;
|
|
||||||
width: $s-8;
|
|
||||||
}
|
|
||||||
div {
|
|
||||||
display: flex;
|
|
||||||
width: 90%;
|
|
||||||
}
|
|
||||||
span {
|
|
||||||
@include textEllipsis;
|
|
||||||
}
|
|
||||||
.component-group-path {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
.component-group-name {
|
|
||||||
color: var(--assets-item-name-foreground-color);
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
color: var(--assets-item-name-foreground-color-hover);
|
|
||||||
.component-group-name {
|
|
||||||
color: var(--assets-item-name-foreground-color-hover);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.component-grid {
|
.component-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(2, $s-124);
|
grid-template-columns: repeat(2, $s-124);
|
||||||
|
@ -393,30 +382,107 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.element-set-title {
|
||||||
|
@include tabTitleTipography;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: $s-32;
|
||||||
|
padding-left: $s-2;
|
||||||
|
color: var(--title-foreground-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Component swap
|
||||||
|
|
||||||
|
.component-swap {
|
||||||
|
padding-top: $s-12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-swap-content {
|
||||||
|
@include flexColumn;
|
||||||
|
gap: $s-16;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fields-wrapper {
|
||||||
|
@include flexColumn;
|
||||||
|
gap: $s-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-field {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: $s-32;
|
||||||
|
border-radius: $br-8;
|
||||||
|
font-family: "worksans", sans-serif;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-name-wrapper {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
}
|
||||||
|
|
||||||
.library-name {
|
.library-name {
|
||||||
@include titleTipography;
|
@include titleTipography;
|
||||||
@include textEllipsis;
|
@include textEllipsis;
|
||||||
margin: $s-20 $s-4 0 $s-12;
|
|
||||||
color: var(--title-foreground-color);
|
color: var(--title-foreground-color);
|
||||||
|
padding: $s-8 0 $s-8 $s-2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.element-set-title {
|
.swap-wrapper {
|
||||||
|
@include flexColumn;
|
||||||
|
gap: $s-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listing-options-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.listing-options {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-group {
|
||||||
@include titleTipography;
|
@include titleTipography;
|
||||||
text-transform: uppercase;
|
display: grid;
|
||||||
margin: $s-16 $s-4 0 $s-12;
|
grid-template-columns: 1fr $s-12;
|
||||||
color: var(--title-foreground-color);
|
height: $s-32;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.component-group-name {
|
||||||
|
@include textEllipsis;
|
||||||
|
color: var(--assets-item-name-foreground-color);
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
color: var(--assets-item-name-foreground-color-hover);
|
||||||
|
.component-group-name {
|
||||||
|
color: var(--assets-item-name-foreground-color-hover);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-button {
|
.arrow-icon {
|
||||||
|
@include flexCenter;
|
||||||
|
height: $s-32;
|
||||||
svg {
|
svg {
|
||||||
stroke: var(--icon-foreground);
|
|
||||||
fill: var(--icon-foreground);
|
|
||||||
height: $s-12;
|
height: $s-12;
|
||||||
width: $s-12;
|
width: $s-12;
|
||||||
cursor: pointer;
|
stroke: var(--icon-foreground);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.path-wrapper {
|
||||||
|
display: flex;
|
||||||
|
max-width: $s-232;
|
||||||
|
padding: $s-8 0 $s-8 $s-2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-group-path {
|
||||||
|
@include textEllipsis;
|
||||||
|
direction: rtl;
|
||||||
|
color: var(--assets-item-name-foreground-color-rest);
|
||||||
|
}
|
||||||
|
|
||||||
// Component annotation
|
// Component annotation
|
||||||
|
|
||||||
.component-annotation {
|
.component-annotation {
|
||||||
|
|
|
@ -221,7 +221,7 @@
|
||||||
(when (and (not multiple?) (or (= :manual cell-mode) (= :area cell-mode)))
|
(when (and (not multiple?) (or (= :manual cell-mode) (= :area cell-mode)))
|
||||||
[:div {:class (stl/css :row)}
|
[:div {:class (stl/css :row)}
|
||||||
[:div {:class (stl/css :grid-coord-group)}
|
[:div {:class (stl/css :grid-coord-group)}
|
||||||
[:span {:class (stl/css :icon)} i/layout-rows]
|
[:span {:class (stl/css :icon)} i/flex-vertical-refactor]
|
||||||
[:div {:class (stl/css :coord-input)}
|
[:div {:class (stl/css :coord-input)}
|
||||||
[:> numeric-input*
|
[:> numeric-input*
|
||||||
{:placeholder "--"
|
{:placeholder "--"
|
||||||
|
@ -236,7 +236,7 @@
|
||||||
:value column-end}]]]
|
:value column-end}]]]
|
||||||
|
|
||||||
[:div {:class (stl/css :grid-coord-group)}
|
[:div {:class (stl/css :grid-coord-group)}
|
||||||
[:span {:class (stl/css :icon)} i/layout-columns]
|
[:span {:class (stl/css :icon)} i/flex-horizontal-refactor]
|
||||||
[:div {:class (stl/css :coord-input :double)}
|
[:div {:class (stl/css :coord-input :double)}
|
||||||
[:> numeric-input*
|
[:> numeric-input*
|
||||||
{:placeholder "--"
|
{:placeholder "--"
|
||||||
|
|
|
@ -112,8 +112,7 @@
|
||||||
(fn [color]
|
(fn [color]
|
||||||
(st/emit! (dch/update-shapes
|
(st/emit! (dch/update-shapes
|
||||||
ids
|
ids
|
||||||
#(assoc-in % [:shadow index :color]
|
#(assoc-in % [:shadow index :color] color)))))
|
||||||
(dissoc color :id :file-id))))))
|
|
||||||
|
|
||||||
detach-color
|
detach-color
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
|
|
@ -147,7 +147,8 @@
|
||||||
:stroke-cap-end stroke-cap-start} index)))))
|
:stroke-cap-end stroke-cap-start} index)))))
|
||||||
on-add-stroke
|
on-add-stroke
|
||||||
(fn [_]
|
(fn [_]
|
||||||
(st/emit! (dc/add-stroke ids {:stroke-style :solid
|
(st/emit! (dc/add-stroke ids {:stroke-alignment :inner
|
||||||
|
:stroke-style :solid
|
||||||
:stroke-color clr/black
|
:stroke-color clr/black
|
||||||
:stroke-opacity 1
|
:stroke-opacity 1
|
||||||
:stroke-width 1}))
|
:stroke-width 1}))
|
||||||
|
|
|
@ -255,6 +255,7 @@
|
||||||
:on-focus on-focus
|
:on-focus on-focus
|
||||||
:on-blur on-blur
|
:on-blur on-blur
|
||||||
:on-change handle-opacity-change
|
:on-change handle-opacity-change
|
||||||
|
:default 100
|
||||||
:min 0
|
:min 0
|
||||||
:max 100}]])]
|
:max 100}]])]
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
@import "refactor/common-refactor.scss";
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
.main-toolbar {
|
.main-toolbar {
|
||||||
|
cursor: initial;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $s-28;
|
top: $s-28;
|
||||||
left: calc(50% - $s-160);
|
left: calc(50% - $s-160);
|
||||||
|
|
|
@ -125,6 +125,7 @@
|
||||||
height: $s-48;
|
height: $s-48;
|
||||||
margin-left: -50%;
|
margin-left: -50%;
|
||||||
padding: $s-8;
|
padding: $s-8;
|
||||||
|
cursor: initial;
|
||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
width: $s-512;
|
width: $s-512;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
@import "refactor/common-refactor.scss";
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
.sub-actions {
|
.sub-actions {
|
||||||
|
cursor: initial;
|
||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $s-12;
|
top: $s-12;
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
height: $s-48;
|
height: $s-48;
|
||||||
margin-left: -50%;
|
margin-left: -50%;
|
||||||
padding: $s-8;
|
padding: $s-8;
|
||||||
|
cursor: initial;
|
||||||
pointer-events: initial;
|
pointer-events: initial;
|
||||||
width: $s-400;
|
width: $s-400;
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,11 @@
|
||||||
(on-frame-leave (:id frame))))
|
(on-frame-leave (:id frame))))
|
||||||
|
|
||||||
main-instance? (ctk/main-instance? frame)
|
main-instance? (ctk/main-instance? frame)
|
||||||
text-pos-x (if (or (:use-for-thumbnail frame) grid-edition? main-instance?) 15 0)]
|
|
||||||
|
text-width (* (:width frame) zoom)
|
||||||
|
show-icon? (and (or (:use-for-thumbnail frame) grid-edition? main-instance?)
|
||||||
|
(not (<= text-width 15)))
|
||||||
|
text-pos-x (if show-icon? 15 0)]
|
||||||
|
|
||||||
(when (not (:hidden frame))
|
(when (not (:hidden frame))
|
||||||
[:g.frame-title {:id (dm/str "frame-title-" (:id frame))
|
[:g.frame-title {:id (dm/str "frame-title-" (:id frame))
|
||||||
|
@ -138,7 +142,7 @@
|
||||||
:transform (vwu/title-transform frame zoom grid-edition?)
|
:transform (vwu/title-transform frame zoom grid-edition?)
|
||||||
:pointer-events (when (:blocked frame) "none")}
|
:pointer-events (when (:blocked frame) "none")}
|
||||||
(cond
|
(cond
|
||||||
(or (:use-for-thumbnail frame) grid-edition? main-instance?)
|
show-icon?
|
||||||
[:svg {:x 0
|
[:svg {:x 0
|
||||||
:y -9
|
:y -9
|
||||||
:width 12
|
:width 12
|
||||||
|
@ -157,9 +161,10 @@
|
||||||
main-instance?
|
main-instance?
|
||||||
[:use {:href "#icon-component-refactor"}])])
|
[:use {:href "#icon-component-refactor"}])])
|
||||||
|
|
||||||
[:text {:x text-pos-x
|
|
||||||
:y 0
|
[:foreignObject {:x text-pos-x
|
||||||
:width (:width frame)
|
:y -11
|
||||||
|
:width (max 0 (- text-width text-pos-x))
|
||||||
:height 20
|
:height 20
|
||||||
:class "workspace-frame-label"
|
:class "workspace-frame-label"
|
||||||
:style {:fill color}
|
:style {:fill color}
|
||||||
|
@ -169,9 +174,11 @@
|
||||||
:on-context-menu on-context-menu
|
:on-context-menu on-context-menu
|
||||||
:on-pointer-enter on-pointer-enter
|
:on-pointer-enter on-pointer-enter
|
||||||
:on-pointer-leave on-pointer-leave}
|
:on-pointer-leave on-pointer-leave}
|
||||||
|
[:div {:class (stl/css :workspace-frame-label)
|
||||||
|
:style {:color color}}
|
||||||
(if show-id?
|
(if show-id?
|
||||||
(dm/str (dm/str (:id frame)) " - " (:name frame))
|
(dm/str (dm/str (:id frame)) " - " (:name frame))
|
||||||
(:name frame))]])))
|
(:name frame))]]])))
|
||||||
|
|
||||||
(mf/defc frame-titles
|
(mf/defc frame-titles
|
||||||
{::mf/wrap-props false
|
{::mf/wrap-props false
|
||||||
|
|
|
@ -62,3 +62,11 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-frame-label {
|
||||||
|
font-size: $fs-12;
|
||||||
|
color: black;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
|
@ -37,14 +37,19 @@
|
||||||
|
|
||||||
;; TODO: REMOVE `VALUE` WHEN COLOR IS INTEGRATED
|
;; TODO: REMOVE `VALUE` WHEN COLOR IS INTEGRATED
|
||||||
(defn color->background [{:keys [color opacity gradient value]}]
|
(defn color->background [{:keys [color opacity gradient value]}]
|
||||||
(let [color (or color value)
|
(let [color (d/nilv color value)
|
||||||
opacity (or opacity 1)]
|
opacity (or opacity 1)]
|
||||||
|
|
||||||
(cond
|
(cond
|
||||||
(and gradient (not= :multiple gradient))
|
(and gradient (not= :multiple gradient))
|
||||||
(gradient->css gradient)
|
(gradient->css gradient)
|
||||||
|
|
||||||
(not= color :multiple)
|
(and (some? color) (not= color :multiple))
|
||||||
(let [[r g b] (cc/hex->rgb (or color value))]
|
(let [color
|
||||||
|
(-> (str/replace color "#" "")
|
||||||
|
(cc/expand-hex)
|
||||||
|
(cc/prepend-hash))
|
||||||
|
[r g b] (cc/hex->rgb color)]
|
||||||
(str/fmt "rgba(%s, %s, %s, %s)" r g b opacity))
|
(str/fmt "rgba(%s, %s, %s, %s)" r g b opacity))
|
||||||
|
|
||||||
:else "transparent")))
|
:else "transparent")))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue