diff --git a/CHANGES.md b/CHANGES.md index b1791255a..3a0659912 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,7 +30,12 @@ - Remove interactions when the destination artboard is deleted [Taiga #1656](https://tree.taiga.io/project/penpot/issue/1656). - Fix problem with fonts that ends with numbers [#940](https://github.com/penpot/penpot/issues/940). - +- Fix problem with imported SVG on editing paths [#971](https://github.com/penpot/penpot/issues/971) +- Fix problem with color picker positioning +- Fix order on color palette [#961](https://github.com/penpot/penpot/issues/961) +- Fix issue when group creation leaves an empty group [#1724](https://tree.taiga.io/project/penpot/issue/1724) +- Fix problem with :multiple for colors and typographies [#1668](https://tree.taiga.io/project/penpot/issue/1668) +- Fix problem with locked shapes when change parents [#974](https://github.com/penpot/penpot/issues/974) ### :arrow_up: Deps updates diff --git a/backend/src/app/rpc/mutations/files.clj b/backend/src/app/rpc/mutations/files.clj index 7ce732eca..d6fb24c59 100644 --- a/backend/src/app/rpc/mutations/files.clj +++ b/backend/src/app/rpc/mutations/files.clj @@ -323,7 +323,8 @@ :has-media-trimmed false} {:id (:id file)}) - (let [params (assoc params :file file)] + (let [params (-> params (assoc :file file + :changes changes))] ;; Send asynchronous notifications (send-notifications cfg params) diff --git a/common/app/common/geom/shapes.cljc b/common/app/common/geom/shapes.cljc index a93c20ad3..db0d51e29 100644 --- a/common/app/common/geom/shapes.cljc +++ b/common/app/common/geom/shapes.cljc @@ -162,7 +162,7 @@ :points points)))) (defn rotation-modifiers - [center shape angle] + [shape center angle] (let [displacement (let [shape-center (gco/center-shape shape)] (-> (gmt/matrix) (gmt/rotate angle center) diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index acbf99e0e..f167e9814 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -134,8 +134,8 @@ width: 100%; display: grid; - grid-template-rows: 20px 1fr; - grid-template-columns: 20px 1fr; + grid-template-rows: 20px 100%; + grid-template-columns: 20px 100%; } .viewport { @@ -145,6 +145,11 @@ overflow: hidden; position: relative; + svg { + widht: 100%; + height: 100%; + } + .viewport-overlays { position: absolute; width: 100%; diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 1d42177dc..5b70d9ce9 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -991,19 +991,22 @@ [[] [] []] ids) - [rchanges uchanges] (relocate-shapes-changes objects - parents - parent-id - page-id - to-index - ids - groups-to-delete - groups-to-unmask - shapes-to-detach - shapes-to-reroot - shapes-to-deroot)] + [rchanges uchanges] + (relocate-shapes-changes objects + parents + parent-id + page-id + to-index + ids + groups-to-delete + groups-to-unmask + shapes-to-detach + shapes-to-reroot + shapes-to-deroot) + + ] (rx/of (dch/commit-changes {:redo-changes rchanges - :undo-chanes uchanges + :undo-changes uchanges :origin it}) (dwc/expand-collapse parent-id)))))) @@ -1058,7 +1061,7 @@ :id id :index cidx}] (rx/of (dch/commit-changes {:redo-changes [rchg] - :undo-chanes [uchg] + :undo-changes [uchg] :origin it})))))) ;; --- Shape / Selection Alignment and Distribution diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index 356be5153..849fdde94 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -32,31 +32,97 @@ (gsh/setup selrect) (assoc :shapes (mapv :id shapes))))) +(defn get-empty-groups + "Retrieve emtpy groups after group creation" + [objects parent-id shapes] + (let [ids (cp/clean-loops objects (into #{} (map :id) shapes)) + parents (->> ids + (reduce #(conj %1 (cp/get-parent %2 objects)) + #{}))] + (loop [current-id (first parents) + to-check (rest parents) + removed-id? ids + result #{}] + + (if-not current-id + ;; Base case, no next element + result + + (let [group (get objects current-id)] + (if (and (not= :frame (:type group)) + (not= current-id parent-id) + (empty? (remove removed-id? (:shapes group)))) + + ;; Adds group to the remove and check its parent + (let [to-check (d/concat [] to-check [(cp/get-parent current-id objects)]) ] + (recur (first to-check) + (rest to-check) + (conj removed-id? current-id) + (conj result current-id))) + + ;; otherwise recur + (recur (first to-check) + (rest to-check) + removed-id? + result))))))) + (defn prepare-create-group - [page-id shapes prefix keep-name] + [objects page-id shapes prefix keep-name] (let [group (make-group shapes prefix keep-name) + frame-id (:frame-id (first shapes)) + parent-id (:parent-id (first shapes)) rchanges [{:type :add-obj :id (:id group) :page-id page-id - :frame-id (:frame-id (first shapes)) - :parent-id (:parent-id (first shapes)) + :frame-id frame-id + :parent-id parent-id :obj group :index (::index (first shapes))} + {:type :mov-objects :page-id page-id :parent-id (:id group) :shapes (mapv :id shapes)}] - uchanges (conj - (mapv (fn [obj] {:type :mov-objects - :page-id page-id - :parent-id (:parent-id obj) - :index (::index obj) - :shapes [(:id obj)]}) - shapes) - {:type :del-obj - :id (:id group) - :page-id page-id})] + uchanges (-> (mapv + (fn [obj] + {:type :mov-objects + :page-id page-id + :parent-id (:parent-id obj) + :index (::index obj) + :shapes [(:id obj)]}) shapes) + (conj + {:type :del-obj + :id (:id group) + :page-id page-id})) + + ids-to-delete (get-empty-groups objects parent-id shapes) + + delete-group + (fn [changes id] + (-> changes + (conj {:type :del-obj + :id id + :page-id page-id}))) + + add-deleted-group + (fn [changes id] + (let [obj (-> (get objects id) + (d/without-keys [:shapes]))] + + (d/concat [{:type :add-obj + :id id + :page-id page-id + :frame-id (:frame-id obj) + :parent-id (:parent-id obj) + :obj obj + :index (::index obj)}] changes))) + + rchanges (->> ids-to-delete + (reduce delete-group rchanges)) + + uchanges (->> ids-to-delete + (reduce add-deleted-group uchanges))] [group rchanges uchanges])) (defn prepare-remove-group @@ -107,7 +173,8 @@ selected (cp/clean-loops objects selected) shapes (shapes-for-grouping objects selected)] (when-not (empty? shapes) - (let [[group rchanges uchanges] (prepare-create-group page-id shapes "Group-" false)] + (let [[group rchanges uchanges] + (prepare-create-group objects page-id shapes "Group-" false)] (rx/of (dch/commit-changes {:redo-changes rchanges :undo-changes uchanges :origin it}) @@ -146,7 +213,7 @@ (if (and (= (count shapes) 1) (= (:type (first shapes)) :group)) [(first shapes) [] []] - (prepare-create-group page-id shapes "Group-" true)) + (prepare-create-group objects page-id shapes "Group-" true)) rchanges (d/concat rchanges [{:type :mod-obj diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 01d8a61a8..c59050916 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -131,7 +131,7 @@ (if (and (= (count shapes) 1) (= (:type (first shapes)) :group)) [(first shapes) [] []] - (dwg/prepare-create-group page-id shapes "Component-" true)) + (dwg/prepare-create-group objects page-id shapes "Component-" true)) [new-shape new-shapes updated-shapes] (make-component-shape group objects file-id) diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs index 1fbf6d28a..3228e0e5e 100644 --- a/frontend/src/app/main/data/workspace/persistence.cljs +++ b/frontend/src/app/main/data/workspace/persistence.cljs @@ -562,7 +562,7 @@ (defn- extract-frame-changes "Process a changes set in a commit to extract the frames that are channging" - [[event objects]] + [[event [old-objects new-objects]]] (let [changes (-> event deref :changes) extract-ids @@ -577,8 +577,11 @@ get-frame-id (fn [id] - (or (and (= :frame (get-in objects [id :type])) id) - (get-in objects [id :frame-id]))) + (let [shape (or (get new-objects id) + (get old-objects id))] + + (or (and (= :frame (:type shape)) id) + (:frame-id shape)))) ;; Extracts the frames and then removes nils and the root frame xform (comp (mapcat extract-ids) @@ -613,7 +616,12 @@ (rx/filter #(or (= :app.main.data.workspace/finalize-page (ptk/type %)) (= ::watch-state-changes (ptk/type %))))) - objects-stream (rx/from-atom refs/workspace-page-objects {:emit-current-value? true}) + objects-stream (->> (rx/concat + (rx/of nil) + (rx/from-atom refs/workspace-page-objects {:emit-current-value? true})) + ;; We need to keep the old-objects so we can check the frame for the + ;; deleted objects + (rx/buffer 2 1)) frame-changes (->> stream (rx/filter dch/commit-changes?) diff --git a/frontend/src/app/main/data/workspace/state_helpers.cljs b/frontend/src/app/main/data/workspace/state_helpers.cljs index 9ce4a3d3f..0710aba55 100644 --- a/frontend/src/app/main/data/workspace/state_helpers.cljs +++ b/frontend/src/app/main/data/workspace/state_helpers.cljs @@ -26,11 +26,18 @@ (get-in state [:workspace-data :components component-id :objects]))) (defn lookup-selected - [state] - (let [objects (lookup-page-objects state) - selected (->> (get-in state [:workspace-local :selected]) - (cp/clean-loops objects)) - is-present? (fn [id] (contains? objects id))] - (into (d/ordered-set) - (filter is-present?) - selected))) + ([state] + (lookup-selected state nil)) + + ([state {:keys [omit-blocked?] + :or {omit-blocked? false}}] + (let [objects (lookup-page-objects state) + selected (->> (get-in state [:workspace-local :selected]) + (cp/clean-loops objects)) + selectable? (fn [id] + (and (contains? objects id) + (or (not omit-blocked?) + (not (get-in objects [id :blocked] false)))))] + (into (d/ordered-set) + (filter selectable?) + selected)))) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index cc0270a95..e726917de 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -242,23 +242,24 @@ ptk/WatchEvent (watch [it state stream] (let [initial (deref ms/mouse-position) - selected (wsh/lookup-selected state) + selected (wsh/lookup-selected state {:omit-blocked? true}) stopper (rx/filter ms/mouse-up? stream)] - (->> ms/mouse-position - (rx/take-until stopper) - (rx/map #(gpt/to-vec initial %)) - (rx/map #(gpt/length %)) - (rx/filter #(> % 1)) - (rx/take 1) - (rx/with-latest vector ms/mouse-position-alt) - (rx/mapcat - (fn [[_ alt?]] - (if alt? - ;; When alt is down we start a duplicate+move - (rx/of (start-move-duplicate initial) - dws/duplicate-selected) - ;; Otherwise just plain old move - (rx/of (start-move initial selected)))))))))) + (when-not (empty? selected) + (->> ms/mouse-position + (rx/take-until stopper) + (rx/map #(gpt/to-vec initial %)) + (rx/map #(gpt/length %)) + (rx/filter #(> % 1)) + (rx/take 1) + (rx/with-latest vector ms/mouse-position-alt) + (rx/mapcat + (fn [[_ alt?]] + (if alt? + ;; When alt is down we start a duplicate+move + (rx/of (start-move-duplicate initial) + dws/duplicate-selected) + ;; Otherwise just plain old move + (rx/of (start-move initial selected))))))))))) (defn start-move-duplicate [from-position] (ptk/reify ::start-move-selected @@ -319,7 +320,8 @@ (watch [it state stream] (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - ids (if (nil? ids) (wsh/lookup-selected state) ids) + selected (wsh/lookup-selected state {:omit-blocked? true}) + ids (if (nil? ids) selected ids) shapes (mapv #(get objects %) ids) stopper (rx/filter ms/mouse-up? stream) layout (get state :workspace-layout) @@ -398,7 +400,7 @@ ptk/WatchEvent (watch [it state stream] (if (= same-event (get-in state [:workspace-local :current-move-selected])) - (let [selected (wsh/lookup-selected state) + (let [selected (wsh/lookup-selected state {:omit-blocked? true}) move-events (->> stream (rx/filter (ptk/type? ::move-selected)) (rx/filter #(= direction (deref %)))) @@ -435,6 +437,8 @@ page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) + ids (->> ids (into #{} (remove #(get-in objects [% :blocked] false)))) + not-frame-id? (fn [shape-id] (let [shape (get objects shape-id)] @@ -457,27 +461,28 @@ ;; shape adjusting their position. (defn set-rotation - ([delta-rotation shapes] - (set-rotation delta-rotation shapes (-> shapes gsh/selection-rect gsh/center-selrect))) + ([angle shapes] + (set-rotation angle shapes (-> shapes gsh/selection-rect gsh/center-selrect))) - ([delta-rotation shapes center] - (letfn [(rotate-shape [objects angle shape center] - (update-in objects [(:id shape) :modifiers] merge (gsh/rotation-modifiers center shape angle))) + ([angle shapes center] + (ptk/reify ::set-rotation + ptk/UpdateEvent + (update [_ state] + (let [objects (wsh/lookup-page-objects state) + id->obj #(get objects %) + get-children (fn [shape] (map id->obj (cp/get-children (:id shape) objects))) - (rotate-around-center [objects angle center shapes] - (reduce #(rotate-shape %1 angle %2 center) objects shapes)) + shapes (->> shapes (into [] (remove #(get % :blocked false)))) - (set-rotation [objects] - (let [id->obj #(get objects %) - get-children (fn [shape] (map id->obj (cp/get-children (:id shape) objects))) - shapes (concat shapes (mapcat get-children shapes))] - (rotate-around-center objects delta-rotation center shapes)))] + shapes (->> shapes (mapcat get-children) (concat shapes)) - (ptk/reify ::set-rotation - ptk/UpdateEvent - (update [_ state] - (let [page-id (:current-page-id state)] - (d/update-in-when state [:workspace-data :pages-index page-id :objects] set-rotation))))))) + update-shape + (fn [modifiers shape] + (let [rotate-modifiers (gsh/rotation-modifiers shape center angle)] + (assoc-in modifiers [(:id shape) :modifiers] rotate-modifiers)))] + (-> state + (update :workspace-modifiers + #(reduce update-shape % shapes)))))))) (defn increase-rotation [ids rotation] (ptk/reify ::increase-rotation @@ -583,7 +588,7 @@ ptk/WatchEvent (watch [it state stream] (let [objects (wsh/lookup-page-objects state) - selected (wsh/lookup-selected state) + selected (wsh/lookup-selected state {:omit-blocked? true}) shapes (map #(get objects %) selected) selrect (gsh/selection-rect (->> shapes (map gsh/transform-shape))) origin (gpt/point (:x selrect) (+ (:y selrect) (/ (:height selrect) 2)))] @@ -600,7 +605,7 @@ ptk/WatchEvent (watch [it state stream] (let [objects (wsh/lookup-page-objects state) - selected (wsh/lookup-selected state) + selected (wsh/lookup-selected state {:omit-blocked? true}) shapes (map #(get objects %) selected) selrect (gsh/selection-rect (->> shapes (map gsh/transform-shape))) origin (gpt/point (+ (:x selrect) (/ (:width selrect) 2)) (:y selrect))] @@ -633,5 +638,5 @@ (ptk/reify ::selected-to-path ptk/WatchEvent (watch [_ state stream] - (let [ids (wsh/lookup-selected state)] + (let [ids (wsh/lookup-selected state {:omit-blocked? true})] (rx/of (dch/update-shapes ids ups/convert-to-path)))))) diff --git a/frontend/src/app/main/ui/shapes/embed.cljs b/frontend/src/app/main/ui/shapes/embed.cljs index fd2de767a..25d00243d 100644 --- a/frontend/src/app/main/ui/shapes/embed.cljs +++ b/frontend/src/app/main/ui/shapes/embed.cljs @@ -22,18 +22,22 @@ (hooks/use-effect-ssr (mf/deps embed? urls) (fn [] - (let [sub (when embed? - (->> (rx/from urls) - (rx/merge-map http/fetch-data-uri) - (rx/reduce conj {}) - (rx/subs (fn [data] - (when-not (= data (mf/ref-val uri-data)) - (mf/set-ref-val! uri-data data) - (reset! state inc))))))] + (let [;; When not active the embedding we return the URI + url-mapping (fn [obs] + (if embed? + (rx/merge-map http/fetch-data-uri obs) + (rx/map identity obs))) + + sub (->> (rx/from urls) + (url-mapping) + (rx/reduce conj {}) + (rx/subs (fn [data] + (when-not (= data (mf/ref-val uri-data)) + (mf/set-ref-val! uri-data data) + (reset! state inc)))))] #(when sub (rx/dispose! sub))))) ;; Use ref so if the urls are cached will return inmediately instead of the ;; next render - (when embed? - (mf/ref-val uri-data)))) + (mf/ref-val uri-data))) diff --git a/frontend/src/app/main/ui/shapes/fill_image.cljs b/frontend/src/app/main/ui/shapes/fill_image.cljs index 8fb66a3ef..986dbc37e 100644 --- a/frontend/src/app/main/ui/shapes/fill_image.cljs +++ b/frontend/src/app/main/ui/shapes/fill_image.cljs @@ -31,7 +31,8 @@ :y y :height height :width width - :patternTransform transform} + :patternTransform transform + :data-loading (str (not (contains? embed uri)))} [:image {:xlinkHref (get embed uri uri) :width width :height height}]])))) diff --git a/frontend/src/app/main/ui/shapes/image.cljs b/frontend/src/app/main/ui/shapes/image.cljs index a886049f1..16f9a84d7 100644 --- a/frontend/src/app/main/ui/shapes/image.cljs +++ b/frontend/src/app/main/ui/shapes/image.cljs @@ -32,7 +32,9 @@ :transform transform :width width :height height - :preserveAspectRatio "none"})) + :preserveAspectRatio "none" + :data-loading (str (not (contains? embed uri)))})) + on-drag-start (fn [event] ;; Prevent browser dragging of the image (dom/prevent-default event))] diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index 7b436bccd..8c15ccb43 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -75,7 +75,8 @@ ;; (uc/hex->rgba fill-color fill-opacity)) [r g b a] (uc/hex->rgba fill-color fill-opacity) - text-color (str/format "rgba(%s, %s, %s, %s)" r g b a) + text-color (when (and (some? fill-color) (some? fill-opacity)) + (str/format "rgba(%s, %s, %s, %s)" r g b a)) fontsdb (deref fonts/fontsdb) base #js {:textDecoration text-decoration diff --git a/frontend/src/app/main/ui/workspace/colorpalette.cljs b/frontend/src/app/main/ui/workspace/colorpalette.cljs index 176cb0d46..d9d4df230 100644 --- a/frontend/src/app/main/ui/workspace/colorpalette.cljs +++ b/frontend/src/app/main/ui/workspace/colorpalette.cljs @@ -209,7 +209,7 @@ (into [] (cond (= selected :recent) (reverse recent-colors) - (= selected :file) (vals file-colors) + (= selected :file) (->> (vals file-colors) (sort-by :name)) :else (library->colors shared-libs selected)))))) (mf/use-effect @@ -222,7 +222,8 @@ (mf/deps file-colors) (fn [] (when (= selected :file) - (reset! current-library-colors (into [] (vals file-colors)))))) + (reset! current-library-colors (into [] (->> (vals file-colors) + (sort-by :name))))))) [:& palette {:left-sidebar? left-sidebar? :current-colors @current-library-colors diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index fa842aa4d..310ece276 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -150,10 +150,6 @@ (dom/prevent-default event) (let [id (:id item)] (cond - (or (:blocked item) - (:hidden item)) - nil - (kbd/shift? event) (st/emit! (dw/shift-select-shapes id)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index 2b46a3bf5..0d6ab2546 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -288,6 +288,8 @@ (fn [changes] (st/emit! (dwl/update-typography (merge typography changes) file-id)))) + multiple? (->> values vals (d/seek #(= % :multiple))) + opts #js {:ids ids :values values :on-change on-change}] @@ -295,7 +297,7 @@ [:div.element-set [:div.element-set-title [:span label] - (when (not typography) + (when (and (not typography) (not multiple?)) [:div.add-page {:on-click on-convert-to-typography} i/close])] (cond diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs index 96e5f4021..be8a067ef 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.cljs @@ -27,7 +27,19 @@ (defn color-picker-callback [color disable-gradient disable-opacity handle-change-color handle-open handle-close] (fn [event] - (let [x (.-clientX event) + (let [color + (cond + (uc/multiple? color) + {:color cp/default-color + :opacity 1} + + (= :multiple (:opacity color)) + (assoc color :opacity 1) + + :else + color) + + x (.-clientX event) y (.-clientY event) props {:x x :y y @@ -98,16 +110,12 @@ handle-click-color (mf/use-callback (mf/deps color) - (let [;; If multiple, we change to default color - color (if (uc/multiple? color) - {:color cp/default-color :opacity 1} - color)] - (color-picker-callback color - disable-gradient - disable-opacity - handle-pick-color - handle-open - handle-close))) + (color-picker-callback color + disable-gradient + disable-opacity + handle-pick-color + handle-open + handle-close)) prev-color (h/use-previous color)] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 259ff36a6..86a049053 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -148,22 +148,23 @@ extract-attrs (fn [[ids values] {:keys [id type shapes content] :as shape}] - (let [props (get-in type->props [type attr-type]) - result (case props - :ignore [ids values] - :shape [(conj ids id) - (merge-attrs values (merge - (empty-map attrs) - (select-keys shape attrs)))] - :text [(conj ids id) - (-> values - (merge-attrs (select-keys shape attrs)) - (merge-attrs (attrs/get-attrs-multi (txt/node-seq content) attrs)))] - :children (let [children (->> (:shapes shape []) (map #(get objects %))) - [new-ids new-values] (get-attrs children objects attr-type)] - [(d/concat ids new-ids) (merge-attrs values new-values)]) - [])] - result))] + (let [props (get-in type->props [type attr-type])] + (case props + :ignore [ids values] + :shape [(conj ids id) + (merge-attrs values (merge + (empty-map attrs) + (select-keys shape attrs)))] + :text [(conj ids id) + (-> values + (merge-attrs (select-keys shape attrs)) + (merge-attrs (merge + (select-keys txt/default-text-attrs attrs) + (attrs/get-attrs-multi (txt/node-seq content) attrs))))] + :children (let [children (->> (:shapes shape []) (map #(get objects %))) + [new-ids new-values] (get-attrs children objects attr-type)] + [(d/concat ids new-ids) (merge-attrs values new-values)]) + [])))] (reduce extract-attrs [[] []] shapes))) (mf/defc options diff --git a/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs b/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs index 9dda3ac8f..f9cc2707c 100644 --- a/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/snap_points.cljs @@ -131,7 +131,7 @@ #(rx/dispose! sub)))) (mf/use-effect - (mf/deps shapes modifiers) + (mf/deps shapes filter-shapes modifiers) (fn [] (rx/push! subject props))) @@ -161,7 +161,8 @@ (map #(get objects %)) (filterv (comp not nil?))) filter-shapes (into #{} - (mapcat #(cp/get-object-with-children % objects)) + (comp (mapcat #(cp/get-object-with-children % objects)) + (map :id)) selected) filter-shapes (fn [id] diff --git a/frontend/src/app/main/ui/workspace/viewport/thumbnail_renderer.cljs b/frontend/src/app/main/ui/workspace/viewport/thumbnail_renderer.cljs index 79b41e0d3..dc6d5eb69 100644 --- a/frontend/src/app/main/ui/workspace/viewport/thumbnail_renderer.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/thumbnail_renderer.cljs @@ -29,16 +29,19 @@ (when node (let [img-node (mf/ref-val thumbnail-img)] (timers/schedule-on-idle - #(if-let [frame-node (dom/get-element (str "shape-" (:id shape)))] - (let [xml (-> (js/XMLSerializer.) - (.serializeToString frame-node) - js/encodeURIComponent - js/unescape - js/btoa) - img-src (str "data:image/svg+xml;base64," xml)] - (obj/set! img-node "src" img-src)) + #(let [frame-node (dom/get-element (str "shape-" (:id shape))) + loading-node (when frame-node + (dom/query frame-node "[data-loading=\"true\"]"))] + (if (and (some? frame-node) (not (some? loading-node))) + (let [xml (-> (js/XMLSerializer.) + (.serializeToString frame-node) + js/encodeURIComponent + js/unescape + js/btoa) + img-src (str "data:image/svg+xml;base64," xml)] + (obj/set! img-node "src" img-src)) - (on-frame-not-found (:id shape)))))))) + (on-frame-not-found (:id shape))))))))) on-image-load (mf/use-callback @@ -108,8 +111,9 @@ (fn [frame-id] ;; If we couldn't find the frame maybe is still rendering. We push the event again ;; after a time - (timers/schedule-on-idle #(dwp/update-frame-thumbnail frame-id)) - (rx/push! next :next)))] + (reset! shape-id nil) + (rx/push! next :next) + (timers/schedule-on-idle (st/emitf (dwp/update-frame-thumbnail frame-id)))))] (mf/use-effect (mf/deps render-frame) diff --git a/frontend/src/app/util/color.cljs b/frontend/src/app/util/color.cljs index 05183e2eb..8899e6f23 100644 --- a/frontend/src/app/util/color.cljs +++ b/frontend/src/app/util/color.cljs @@ -135,7 +135,7 @@ :else "transparent"))) -(defn multiple? [{:keys [id file-id value color gradient]}] +(defn multiple? [{:keys [id file-id value color gradient opacity]}] (or (= value :multiple) (= color :multiple) (= gradient :multiple) diff --git a/frontend/src/app/util/path/subpaths.cljs b/frontend/src/app/util/path/subpaths.cljs index 3337b6c88..010f1343a 100644 --- a/frontend/src/app/util/path/subpaths.cljs +++ b/frontend/src/app/util/path/subpaths.cljs @@ -22,7 +22,10 @@ (defn add-subpath-command "Adds a command to the subpath" [subpath command] - (let [p (upc/command->point command)] + (let [command (if (= :close-path (:command command)) + (upc/make-line-to (:from subpath)) + command) + p (upc/command->point command)] (-> subpath (assoc :to p) (update :data conj command)))) diff --git a/frontend/src/app/util/timers.cljs b/frontend/src/app/util/timers.cljs index 4675f0262..be8cfdbc8 100644 --- a/frontend/src/app/util/timers.cljs +++ b/frontend/src/app/util/timers.cljs @@ -34,12 +34,13 @@ (-dispose [_] (js/clearInterval sem))))) -(if (and (exists? js/window) (.-requestIdleCallback js/window)) +(if (and (exists? js/window) + (.-requestIdleCallback js/window)) (do (def ^:private request-idle-callback #(js/requestIdleCallback %)) (def ^:private cancel-idle-callback #(js/cancelIdleCallback %))) (do - (def ^:private request-idle-callback #(js/setTimeout % 100)) + (def ^:private request-idle-callback #(js/setTimeout % 250)) (def ^:private cancel-idle-callback #(js/clearTimeout %)))) (defn schedule-on-idle