From a9303c37c4ab7edd1412298ba3c7d4c3b4290135 Mon Sep 17 00:00:00 2001 From: "alonso.torres" <alonso.torres@kaleidos.net> Date: Tue, 31 May 2022 11:21:00 +0200 Subject: [PATCH] :sparkles: Allow for nested frames --- common/src/app/common/pages.cljc | 4 +- common/src/app/common/pages/changes.cljc | 2 +- common/src/app/common/pages/common.cljc | 2 + common/src/app/common/pages/focus.cljc | 15 +- common/src/app/common/pages/helpers.cljc | 95 ++++++- common/src/app/common/pages/indices.cljc | 16 +- common/src/app/common/spec/shape.cljc | 9 +- frontend/src/app/main/constants.cljs | 167 ++++++++++++ frontend/src/app/main/data/workspace.cljs | 24 +- .../src/app/main/data/workspace/common.cljs | 32 +-- .../app/main/data/workspace/drawing/box.cljs | 7 +- .../src/app/main/data/workspace/groups.cljs | 1 - .../src/app/main/data/workspace/indices.cljs | 78 ++++++ .../data/workspace/indices/object_tree.cljs | 28 ++ frontend/src/app/main/ui/shapes/frame.cljs | 12 +- .../app/main/ui/workspace/shapes/frame.cljs | 7 +- .../sidebar/options/menus/measures.cljs | 249 +++++------------- .../app/main/ui/workspace/viewport/hooks.cljs | 3 +- .../main/ui/workspace/viewport/selection.cljs | 2 +- .../main/ui/workspace/viewport/widgets.cljs | 20 +- frontend/src/app/worker/selection.cljs | 38 ++- frontend/translations/en.po | 6 + frontend/translations/es.po | 6 + 23 files changed, 540 insertions(+), 283 deletions(-) create mode 100644 frontend/src/app/main/data/workspace/indices.cljs create mode 100644 frontend/src/app/main/data/workspace/indices/object_tree.cljs diff --git a/common/src/app/common/pages.cljc b/common/src/app/common/pages.cljc index 0e6a91a94..e500d372d 100644 --- a/common/src/app/common/pages.cljc +++ b/common/src/app/common/pages.cljc @@ -26,8 +26,8 @@ (dm/export focus/is-in-focus?) ;; Indices -(dm/export indices/calculate-z-index) -(dm/export indices/update-z-index) +#_(dm/export indices/calculate-z-index) +#_(dm/export indices/update-z-index) (dm/export indices/generate-child-all-parents-index) (dm/export indices/generate-child-parent-index) (dm/export indices/create-clip-index) diff --git a/common/src/app/common/pages/changes.cljc b/common/src/app/common/pages/changes.cljc index 7d511d9ff..393791cd4 100644 --- a/common/src/app/common/pages/changes.cljc +++ b/common/src/app/common/pages/changes.cljc @@ -211,7 +211,7 @@ (let [invalid-targets (calculate-invalid-targets objects shape-id)] (and (contains? objects shape-id) (not (invalid-targets parent-id)) - (cph/valid-frame-target? objects parent-id shape-id)))) + #_(cph/valid-frame-target? objects parent-id shape-id)))) (insert-items [prev-shapes index shapes] (let [prev-shapes (or prev-shapes [])] diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index c412176f6..8756ac472 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -82,6 +82,8 @@ :r1 :r2 :r3 :r4 :selrect :points + :show-content + :hide-in-viewer :opacity :blend-mode diff --git a/common/src/app/common/pages/focus.cljc b/common/src/app/common/pages/focus.cljc index df0f2d351..f4dda2b97 100644 --- a/common/src/app/common/pages/focus.cljc +++ b/common/src/app/common/pages/focus.cljc @@ -13,21 +13,16 @@ (defn focus-objects [objects focus] - (let [[ids-with-children z-index] + (let [ids-with-children (when (d/not-empty? focus) - [(into (conj focus uuid/zero) - (mapcat (partial cph/get-children-ids objects)) - focus) - (cpi/calculate-z-index objects)]) - - sort-by-z-index - (fn [coll] - (->> coll (sort-by (fn [a b] (- (get z-index a) (get z-index b))))))] + (into (conj focus uuid/zero) + (mapcat (partial cph/get-children-ids objects)) + focus))] (cond-> objects (some? ids-with-children) (-> (select-keys ids-with-children) - (assoc-in [uuid/zero :shapes] (sort-by-z-index focus)))))) + (assoc-in [uuid/zero :shapes] (cph/sort-z-index objects focus)))))) (defn filter-not-focus [objects focus ids] diff --git a/common/src/app/common/pages/helpers.cljc b/common/src/app/common/pages/helpers.cljc index b768680eb..901b43812 100644 --- a/common/src/app/common/pages/helpers.cljc +++ b/common/src/app/common/pages/helpers.cljc @@ -141,6 +141,38 @@ (keep lookup))))) (defn get-frames-ids + "Retrieves all frame objects as vector. It is not implemented in + function of `get-immediate-children` for performance reasons. This + function is executed in the render hot path." + [objects] + (let [lookup (d/getf objects) + xform (comp (remove #(= uuid/zero %)) + (keep lookup) + (filter frame-shape?) + (map :id))] + (->> (keys objects) + (into [] xform)))) + +(defn get-frames + "Retrieves all frame objects as vector. It is not implemented in + function of `get-immediate-children` for performance reasons. This + function is executed in the render hot path." + [objects] + (let [lookup (d/getf objects) + xform (comp (remove #(= uuid/zero %)) + (keep lookup) + (filter frame-shape?))] + (->> (keys objects) + (into [] xform)))) + +(defn get-nested-frames + [objects frame-id] + (into #{} + (comp (filter frame-shape?) + (map :id)) + (get-children objects frame-id))) + +(defn get-root-frames-ids "Retrieves all frame objects as vector. It is not implemented in function of `get-immediate-children` for performance reasons. This function is executed in the render hot path." @@ -152,7 +184,7 @@ (->> (:shapes (lookup uuid/zero)) (into [] xform)))) -(defn get-frames +(defn get-root-frames "Retrieves all frame objects as vector. It is not implemented in function of `get-immediate-children` for performance reasons. This function is executed in the render hot path." @@ -163,15 +195,62 @@ (->> (:shapes (lookup uuid/zero)) (into [] xform)))) +(defn- get-base + [objects id-a id-b] + + (let [parents-a (reverse (get-parents-seq objects id-a)) + parents-b (reverse (get-parents-seq objects id-b)) + + [base base-child-a base-child-b] + (loop [parents-a (rest parents-a) + parents-b (rest parents-b) + base uuid/zero] + (if (not= (first parents-a) (first parents-b)) + [base (first parents-a) (first parents-b)] + (recur (rest parents-a) (rest parents-b) (first parents-a)))) + + index-base-a (when base-child-a (get-position-on-parent objects base-child-a)) + index-base-b (when base-child-b (get-position-on-parent objects base-child-b))] + + [base index-base-a index-base-b])) + +(defn is-shape-over-shape? + [objects base-shape-id over-shape-id] + + (let [[base parent-a parent-b] (get-base objects base-shape-id over-shape-id)] + (cond + (= base base-shape-id) + ;; over-shape is a child of base-shape. Will be over if base is a root-frame + (= uuid/zero (get-in objects [base-shape-id :parent-id])) + + (= base over-shape-id) + (not= uuid/zero (get-in objects [over-shape-id :parent-id])) + + :else + (< parent-a parent-b)))) + +(defn sort-z-index + [objects ids] + (letfn [(comp [id-a id-b] + (cond + (= id-a id-b) 0 + (is-shape-over-shape? objects id-a id-b) 1 + :else -1))] + (sort comp ids))) + (defn frame-id-by-position [objects position] - (let [frames (get-frames objects)] - (or - (->> frames - (reverse) - (d/seek #(and position (gsh/has-point? % position))) - :id) - uuid/zero))) + (let [frames (->> (get-frames objects) + (filter #(and position (gsh/has-point? % position)))) + + top-frame + (reduce (fn [current-top frame] + (if (is-shape-over-shape? objects (:id current-top) (:id frame)) + frame + current-top)) + (first frames) + (rest frames))] + (or (:id top-frame) uuid/zero))) (declare indexed-shapes) diff --git a/common/src/app/common/pages/indices.cljc b/common/src/app/common/pages/indices.cljc index 3a5a852ee..571de3613 100644 --- a/common/src/app/common/pages/indices.cljc +++ b/common/src/app/common/pages/indices.cljc @@ -11,10 +11,12 @@ [app.common.uuid :as uuid] [clojure.set :as set])) -(defn calculate-frame-z-index +#_(defn calculate-frame-z-index [z-index frame-id base-idx objects] - (let [is-frame? (fn [id] (= :frame (get-in objects [id :type]))) + (let [is-root-frame? (fn [id] + (and (= :frame (get-in objects [id :type])) + (= uuid/zero (get-in objects [id :parent-id])))) children (or (get-in objects [frame-id :shapes]) [])] (if (empty? children) @@ -25,8 +27,8 @@ z-index z-index] (let [children (get-in objects [current :shapes]) - is-frame? (is-frame? current) - pending (if (not is-frame?) + is-root-frame? (is-root-frame? current) + pending (if (not is-root-frame?) (d/concat-vec pending children) pending)] @@ -41,12 +43,12 @@ ;; internal z-index. To calculate the "final" z-index we add the shape z-index with ;; the z-index of its frame. This way we can update the z-index per frame without ;; the need of recalculate all the frames -(defn calculate-z-index +#_(defn calculate-z-index "Given a collection of shapes calculates their z-index. Greater index means is displayed over other shapes with less index." [objects] - (let [frames (cph/get-frames objects) + (let [frames (cph/get-root-frames objects) by-frame (cph/objects-by-frame objects) frame-base-idx (d/update-vals by-frame count) @@ -57,7 +59,7 @@ (fn [z-index {:keys [id]}] (calculate-frame-z-index z-index id (get frame-base-idx id) objects)) z-index)))) -(defn update-z-index +#_(defn update-z-index "Updates the z-index given a set of ids to change and the old and new objects representations" [z-index changed-ids old-objects new-objects] diff --git a/common/src/app/common/spec/shape.cljc b/common/src/app/common/spec/shape.cljc index 42ae67f60..0c1deb156 100644 --- a/common/src/app/common/spec/shape.cljc +++ b/common/src/app/common/spec/shape.cljc @@ -52,6 +52,8 @@ (s/def ::fill-color-ref-id (s/nilable uuid?)) (s/def ::hide-fill-on-export boolean?) +(s/def ::show-content boolean?) +(s/def ::hide-in-viewer boolean?) (s/def ::file-thumbnail boolean?) (s/def ::masked-group? boolean?) @@ -254,8 +256,7 @@ :internal.shape.text.position-data/rtl :internal.shape.text.position-data/text :internal.shape.text.position-data/text-decoration - :internal.shape.text.position-data/text-transform] - )) + :internal.shape.text.position-data/text-transform])) (s/def :internal.shape.text.position-data/x ::us/safe-number) (s/def :internal.shape.text.position-data/y ::us/safe-number) @@ -303,7 +304,9 @@ (defmethod shape-spec :frame [_] (s/and ::shape-attrs (s/keys :opt-un [::file-thumbnail - ::hide-fill-on-export]))) + ::hide-fill-on-export + ::show-content + ::hide-in-viewer]))) (s/def ::shape (s/and (s/multi-spec shape-spec :type) diff --git a/frontend/src/app/main/constants.cljs b/frontend/src/app/main/constants.cljs index 84087c5e0..5f9e84486 100644 --- a/frontend/src/app/main/constants.cljs +++ b/frontend/src/app/main/constants.cljs @@ -25,3 +25,170 @@ (def has-layout-item false) +(def size-presets + [{:name "APPLE"} + {:name "iPhone 12/12 Pro" + :width 390 + :height 844} + {:name "iPhone 12 Mini" + :width 360 + :height 780} + {:name "iPhone 12 Pro Max" + :width 428 + :height 926} + {:name "iPhone X/XS/11 Pro" + :width 375 + :height 812} + {:name "iPhone XS Max/XR/11" + :width 414 + :height 896} + {:name "iPhone 6/7/8 Plus" + :width 414 + :height 736} + {:name "iPhone 6/7/8/SE2" + :width 375 + :height 667} + {:name "iPhone 5/SE" + :width 320 + :height 568} + {:name "iPad" + :width 768 + :height 1024} + {:name "iPad Pro 10.5in" + :width 834 + :height 1112} + {:name "iPad Pro 12.9in" + :width 1024 + :height 1366} + {:name "Watch 44mm" + :width 368 + :height 448} + {:name "Watch 42mm" + :width 312 + :height 390} + {:name "Watch 40mm" + :width 324 + :height 394} + {:name "Watch 38mm" + :width 272 + :height 340} + + {:name "ANDROID"} + {:name "Mobile" + :width 360 + :height 640} + {:name "Tablet" + :width 768 + :height 1024} + {:name "Google Pixel 4a/5" + :width 393 + :height 851} + {:name "Samsung Galaxy S20+" + :width 384 + :height 854} + {:name "Samsung Galaxy A71/A51" + :width 412 + :height 914} + + {:name "MICROSOFT"} + {:name "Surface Pro 3" + :width 1440 + :height 960} + {:name "Surface Pro 4/5/6/7" + :width 1368 + :height 912} + + {:name "ReMarkable"} + {:name "Remarkable 2" + :width 840 + :height 1120} + + {:name "WEB"} + {:name "Web 1280" + :width 1280 + :height 800} + {:name "Web 1366" + :width 1366 + :height 768} + {:name "Web 1024" + :width 1024 + :height 768} + {:name "Web 1920" + :width 1920 + :height 1080} + + {:name "PRINT (96dpi)"} + {:name "A0" + :width 3179 + :height 4494} + {:name "A1" + :width 2245 + :height 3179} + {:name "A2" + :width 1587 + :height 2245} + {:name "A3" + :width 1123 + :height 1587} + {:name "A4" + :width 794 + :height 1123} + {:name "A5" + :width 559 + :height 794} + {:name "A6" + :width 397 + :height 559} + {:name "Letter" + :width 816 + :height 1054} + {:name "DIN Lang" + :width 835 + :height 413} + + {:name "SOCIAL MEDIA"} + {:name "Instagram profile" + :width 320 + :height 320} + {:name "Instagram post" + :width 1080 + :height 1080} + {:name "Instagram story" + :width 1080 + :height 1920} + {:name "Facebook profile" + :width 720 + :height 720} + {:name "Facebook cover" + :width 820 + :height 312} + {:name "Facebook post" + :width 1200 + :height 630} + {:name "LinkedIn profile" + :width 400 + :height 400} + {:name "LinkedIn cover" + :width 1584 + :height 396} + {:name "LinkedIn post" + :width 1200 + :height 627} + {:name "Twitter profile" + :width 400 + :height 400} + {:name "Twitter header" + :width 1500 + :height 500} + {:name "Twitter post" + :width 1024 + :height 512} + {:name "YouTube profile" + :width 800 + :height 800} + {:name "YouTube banner" + :width 2560 + :height 1440} + {:name "YouTube thumb" + :width 1280 + :height 720}]) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 6ada6cc96..abb8c2244 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -6,6 +6,8 @@ (ns app.main.data.workspace (:require + [app.main.data.workspace.indices :as dwidx] + [app.common.attrs :as attrs] [app.common.data :as d] [app.common.data.macros :as dm] @@ -127,7 +129,8 @@ team-id (dm/get-in bundle [:project :team-id])] (rx/merge (rx/of (dwn/initialize team-id file-id) - (dwp/initialize-file-persistence file-id)) + (dwp/initialize-file-persistence file-id) + (dwidx/start-indexing file-id)) (->> stream (rx/filter #(= ::dwc/index-initialized %)) @@ -194,6 +197,7 @@ (watch [_ _ _] (rx/merge (rx/of (dwn/finalize file-id)) + (rx/of (dwidx/stop-indexing file-id)) (->> (rx/of ::dwp/finalize) (rx/observe-on :async)))))) @@ -1214,16 +1218,14 @@ ;; selected and its parents objects (cph/selected-subtree objects selected) - z-index (cp/calculate-z-index objects) - z-values (->> selected - (map #(vector % - (+ (get z-index %) - (get z-index (get-in objects [% :frame-id])))))) - selected - (->> z-values - (sort-by second) - (map first) - (into (d/ordered-set)))] + ;;z-index (cp/calculate-z-index objects) + ;;z-values (->> selected + ;; (map #(vector % + ;; (+ (get z-index %) + ;; (get z-index (get-in objects [% :frame-id])))))) + + selected (-> (cph/sort-z-index objects selected) + (into (d/ordered-set)))] (assoc data :selected selected))) diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index bdc2b6110..5032a70fe 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -261,25 +261,21 @@ (defn get-shape-layer-position [objects selected attrs] - (if (= :frame (:type attrs)) - ;; Frames are always positioned on the root frame - [uuid/zero uuid/zero nil] + ;; Calculate the frame over which we're drawing + (let [position @ms/mouse-position + frame-id (:frame-id attrs (cph/frame-id-by-position objects position)) + shape (when-not (empty? selected) + (cph/get-base-shape objects selected))] - ;; Calculate the frame over which we're drawing - (let [position @ms/mouse-position - frame-id (:frame-id attrs (cph/frame-id-by-position objects position)) - shape (when-not (empty? selected) - (cph/get-base-shape objects selected))] + ;; When no shapes has been selected or we're over a different frame + ;; we add it as the latest shape of that frame + (if (or (not shape) (not= (:frame-id shape) frame-id)) + [frame-id frame-id nil] - ;; When no shapes has been selected or we're over a different frame - ;; we add it as the latest shape of that frame - (if (or (not shape) (not= (:frame-id shape) frame-id)) - [frame-id frame-id nil] - - ;; Otherwise, we add it to next to the selected shape - (let [index (cph/get-position-on-parent objects (:id shape)) - {:keys [frame-id parent-id]} shape] - [frame-id parent-id (inc index)]))))) + ;; Otherwise, we add it to next to the selected shape + (let [index (cph/get-position-on-parent objects (:id shape)) + {:keys [frame-id parent-id]} shape] + [frame-id parent-id (inc index)])))) (defn make-new-shape [attrs objects selected] @@ -325,7 +321,7 @@ selected) changes (-> (pcb/empty-changes it page-id) - (pcb/add-object shape {:index (when (= :frame (:type shape)) 0)}))] + (pcb/add-object shape #_{:index (when (= :frame (:type shape)) 0)}))] (rx/concat (rx/of (dch/commit-changes changes) diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs index c2eeefc70..b5dcaf107 100644 --- a/frontend/src/app/main/data/workspace/drawing/box.cljs +++ b/frontend/src/app/main/data/workspace/drawing/box.cljs @@ -65,12 +65,7 @@ focus (:workspace-focus-selected state) zoom (get-in state [:workspace-local :zoom] 1) - frames (cph/get-frames objects) - fid (or (->> frames - (filter #(gsh/has-point? % initial)) - first - :id) - uuid/zero) + fid (cph/frame-id-by-position objects initial) shape (-> state (get-in [:workspace-drawing :object]) diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index fc7d905be..3dd032e0c 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -21,7 +21,6 @@ [objects selected] (->> selected (map #(get objects %)) - (filter #(not= :frame (:type %))) (map #(assoc % ::index (cph/get-position-on-parent objects (:id %)))) (sort-by ::index))) diff --git a/frontend/src/app/main/data/workspace/indices.cljs b/frontend/src/app/main/data/workspace/indices.cljs new file mode 100644 index 000000000..4cf4fb39a --- /dev/null +++ b/frontend/src/app/main/data/workspace/indices.cljs @@ -0,0 +1,78 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.main.data.workspace.indices + (:require + [app.main.data.workspace.state-helpers :as wsh] + [app.main.data.workspace.indices.object-tree :as dwi-object-tree] + [app.main.refs :as refs] + [app.main.data.workspace.changes :as dwc] + [beicon.core :as rx] + [app.common.data :as d] + [potok.core :as ptk])) + +(def stop-indexing? (ptk/type? ::stop-indexing)) + +(def objects-changes #{:add-obj :mod-obj :del-obj :mov-objects}) + +(defn stop-indexing + [file-id] + (ptk/reify ::stop-indexing + ptk/UpdateEvent + (update [_ state] + (-> state + (dissoc :index-object-tree))))) + +(defn process-changes + "Simplify changes so we have only the type of operation and the ids" + [changes] + (->> changes + (filter #(contains? objects-changes (:type %))) + (mapcat (fn [{:keys [type id shapes]}] + (if (some? shapes) + (->> shapes (map #(vector type %))) + [[type id]]))))) + +(defn update-indexing + [change-type shape-id old-objects new-objects] + (ptk/reify ::update-indexing + ptk/UpdateEvent + (update [_ state] + (let [objects (wsh/lookup-page-objects state)] + (-> state + (update :index-object-tree dwi-object-tree/update-index shape-id change-type old-objects new-objects)))))) + +(defn start-indexing + [file-id] + + (ptk/reify ::start-indexing + ptk/UpdateEvent + (update [_ state] + (let [objects (wsh/lookup-page-objects state)] + (-> state + (assoc :index-object-tree (dwi-object-tree/init-index objects))))) + + ptk/WatchEvent + (watch [_ state stream] + (let [stopper (->> stream (rx/filter stop-indexing?) (rx/take 1)) + objects-delta (->> (rx/from-atom refs/workspace-page-objects {:emit-current-value? true}) (rx/buffer 2 1))] + (->> stream + (rx/filter dwc/commit-changes?) + (rx/flat-map #(->> % deref :changes process-changes)) + (rx/with-latest-from objects-delta) + (rx/map (fn [[[type id] [objects-old objects-new]]] + (update-indexing type id objects-old objects-new))) + #_(rx/tap (fn [[[type id] [objects-old objects-new]]] + (let [obj-old (get objects-old id) + obj-new (get objects-new id)] + (prn ">change" (or (:name obj-old) (:name obj-new))) + (prn " > " type) + (.log js/console " >" (clj->js obj-old)) + (.log js/console " >" (clj->js obj-new)) + + ))) + (rx/take-until stopper) + (rx/ignore)))))) diff --git a/frontend/src/app/main/data/workspace/indices/object_tree.cljs b/frontend/src/app/main/data/workspace/indices/object_tree.cljs new file mode 100644 index 000000000..e772b0ad8 --- /dev/null +++ b/frontend/src/app/main/data/workspace/indices/object_tree.cljs @@ -0,0 +1,28 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.main.data.workspace.indices.object-tree + (:require + [app.common.pages.helpers :as cph] + )) + +(defn objects-tree + [objects] + + + + ) + +(defn init-index + [objects] + + + + ) + +(defn update-index + [index shape-id change-type old-objects new-objects] + ) diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 8e4d07866..3275e6ab7 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -45,7 +45,7 @@ [props] (let [shape (obj/get props "shape")] (when (:thumbnail shape) - (let [{:keys [x y width height]} shape + (let [{:keys [x y width height show-content]} shape transform (gsh/transform-matrix shape) props (-> (attrs/extract-style-attrs shape) (obj/merge! @@ -59,8 +59,9 @@ render-id (mf/use-ctx muc/render-ctx)] [:* - [:g {:clip-path (frame-clip-url shape render-id)} - [:& frame-clip-def {:shape shape :render-id render-id}] + [:g {:clip-path (when (not show-content) (frame-clip-url shape render-id))} + (when (not show-content) + [:& frame-clip-def {:shape shape :render-id render-id}]) [:& shape-fills {:shape shape} (if path? [:> :path props] @@ -88,7 +89,7 @@ [props] (let [childs (unchecked-get props "childs") shape (unchecked-get props "shape") - {:keys [x y width height]} shape + {:keys [x y width height show-content]} shape transform (gsh/transform-matrix shape) @@ -104,7 +105,8 @@ render-id (mf/use-ctx muc/render-ctx)] [:* - [:g {:clip-path (frame-clip-url shape render-id)} + [:g {:clip-path (when (not show-content) + (frame-clip-url shape render-id))} [:& shape-fills {:shape shape} (if path? [:> :path props] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index ec70154f7..7f23daf08 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -6,6 +6,8 @@ (ns app.main.ui.workspace.shapes.frame (:require + [app.main.store :as st] + [app.main.data.workspace.state-helpers :as wsh] [app.common.data :as d] [app.common.data.macros :as dm] [app.common.uuid :as uuid] @@ -45,8 +47,7 @@ [new-props old-props] (and (= (unchecked-get new-props "thumbnail?") (unchecked-get old-props "thumbnail?")) - (= (unchecked-get new-props "shape") (unchecked-get old-props "shape")) - (= (unchecked-get new-props "objects") (unchecked-get old-props "objects")))) + (= (unchecked-get new-props "shape") (unchecked-get old-props "shape")))) (defn frame-wrapper-factory [shape-wrapper] @@ -59,7 +60,7 @@ (let [shape (unchecked-get props "shape") thumbnail? (unchecked-get props "thumbnail?") - objects (unchecked-get props "objects") + objects (wsh/lookup-page-objects @st/state) render-id (mf/use-memo #(str (uuid/next))) fonts (mf/use-memo (mf/deps shape objects) #(ff/shape->fonts shape objects)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index 0977b1a63..898339807 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.workspace.sidebar.options.menus.measures (:require + [app.main.constants :refer [size-presets]] [app.common.data :as d] [app.common.geom.shapes :as gsh] [app.common.spec.radius :as ctr] @@ -30,12 +31,14 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect - :points]) + :points + :show-content + :hide-in-viewer]) (def ^:private type->options {:bool #{:size :position :rotation} :circle #{:size :position :rotation} - :frame #{:presets :size :position :radius} + :frame #{:presets :size :position :radius :clip-content :show-in-viewer} :group #{:size :position :rotation} :image #{:size :position :rotation :radius} :path #{:size :position :rotation} @@ -43,8 +46,6 @@ :svg-raw #{:size :position :rotation} :text #{:size :position :rotation}}) -(declare +size-presets+) - ;; -- User/drawing coords (mf/defc measures-menu [{:keys [ids ids-with-children values type all-types shape] :as props}] @@ -103,6 +104,9 @@ radius-multi? (mf/use-state nil) radius-input-ref (mf/use-ref nil) + clip-content-ref (mf/use-ref nil) + show-in-viewer-ref (mf/use-ref nil) + on-preset-selected (fn [width height] (st/emit! (udw/update-dimensions ids :width width) @@ -146,13 +150,13 @@ change-radius (mf/use-callback - (mf/deps ids-with-children) - (fn [update-fn] - (dch/update-shapes ids-with-children - (fn [shape] - (if (ctr/has-radius? shape) - (update-fn shape) - shape))))) + (mf/deps ids-with-children) + (fn [update-fn] + (dch/update-shapes ids-with-children + (fn [shape] + (if (ctr/has-radius? shape) + (update-fn shape) + shape))))) on-switch-to-radius-1 (mf/use-callback @@ -200,17 +204,38 @@ on-radius-r3-change #(on-radius-4-change % :r3) on-radius-r4-change #(on-radius-4-change % :r4) + on-change-clip-content + (mf/use-callback + (mf/deps ids) + (fn [event] + (let [value (-> event dom/get-target dom/checked?)] + (dch/update-shapes ids (fn [shape]) (assoc shape :show-content (not value)))))) + + on-change-clip-content + (mf/use-callback + (mf/deps ids) + (fn [event] + (let [value (-> event dom/get-target dom/checked?)] + (st/emit! (dch/update-shapes ids (fn [shape] (assoc shape :show-content (not value)))))))) + + on-change-show-in-viewer + (mf/use-callback + (mf/deps ids) + (fn [event] + (let [value (-> event dom/get-target dom/checked?)] + (st/emit! (dch/update-shapes ids (fn [shape] (assoc shape :hide-in-viewer (not value)))))))) + select-all #(-> % (dom/get-target) (.select))] - (mf/use-layout-effect - (mf/deps radius-mode @radius-multi?) - (fn [] - (when (and (= radius-mode :radius-1) - (= @radius-multi? false)) - ;; when going back from radius-multi to normal radius-1, - ;; restore focus to the newly created numeric-input - (let [radius-input (mf/ref-val radius-input-ref)] - (dom/focus! radius-input))))) + (mf/use-layout-effect + (mf/deps radius-mode @radius-multi?) + (fn [] + (when (and (= radius-mode :radius-1) + (= @radius-multi? false)) + ;; when going back from radius-multi to normal radius-1, + ;; restore focus to the newly created numeric-input + (let [radius-input (mf/ref-val radius-input-ref)] + (dom/focus! radius-input))))) [:* [:div.element-set @@ -226,7 +251,7 @@ [:& dropdown {:show @show-presets-dropdown? :on-close #(reset! show-presets-dropdown? false)} [:ul.custom-select-dropdown - (for [size-preset +size-presets+] + (for [size-preset size-presets] (if-not (:width size-preset) [:li.dropdown-label {:key (:name size-preset)} [:span (:name size-preset)]] @@ -367,172 +392,30 @@ :min 0 :on-click select-all :on-change on-radius-r4-change - :value (:r4 values)}]]])])]]])) + :value (:r4 values)}]]])]) - (def +size-presets+ - [{:name "APPLE"} - {:name "iPhone 12/12 Pro" - :width 390 - :height 844} - {:name "iPhone 12 Mini" - :width 360 - :height 780} - {:name "iPhone 12 Pro Max" - :width 428 - :height 926} - {:name "iPhone X/XS/11 Pro" - :width 375 - :height 812} - {:name "iPhone XS Max/XR/11" - :width 414 - :height 896} - {:name "iPhone 6/7/8 Plus" - :width 414 - :height 736} - {:name "iPhone 6/7/8/SE2" - :width 375 - :height 667} - {:name "iPhone 5/SE" - :width 320 - :height 568} - {:name "iPad" - :width 768 - :height 1024} - {:name "iPad Pro 10.5in" - :width 834 - :height 1112} - {:name "iPad Pro 12.9in" - :width 1024 - :height 1366} - {:name "Watch 44mm" - :width 368 - :height 448} - {:name "Watch 42mm" - :width 312 - :height 390} - {:name "Watch 40mm" - :width 324 - :height 394} - {:name "Watch 38mm" - :width 272 - :height 340} + (when (options :clip-content) + [:div.input-checkbox + [:input {:type "checkbox" + :id "clip-content" + :ref clip-content-ref + :checked (not (:show-content values)) + :on-change on-change-clip-content}] - {:name "ANDROID"} - {:name "Mobile" - :width 360 - :height 640} - {:name "Tablet" - :width 768 - :height 1024} - {:name "Google Pixel 4a/5" - :width 393 - :height 851} - {:name "Samsung Galaxy S20+" - :width 384 - :height 854} - {:name "Samsung Galaxy A71/A51" - :width 412 - :height 914} + [:label {:for "clip-content"} + (tr "workspace.options.clip-content")]]) - {:name "MICROSOFT"} - {:name "Surface Pro 3" - :width 1440 - :height 960} - {:name "Surface Pro 4/5/6/7" - :width 1368 - :height 912} + (when (options :show-in-viewer) + [:div.input-checkbox + [:input {:type "checkbox" + :id "show-in-viewer" + :ref show-in-viewer-ref + :checked (not (:hide-in-viewer values)) + :on-change on-change-show-in-viewer}] - {:name "ReMarkable"} - {:name "Remarkable 2" - :width 840 - :height 1120} + [:label {:for "show-in-viewer"} + (tr "workspace.options.show-in-viewer")]]) - {:name "WEB"} - {:name "Web 1280" - :width 1280 - :height 800} - {:name "Web 1366" - :width 1366 - :height 768} - {:name "Web 1024" - :width 1024 - :height 768} - {:name "Web 1920" - :width 1920 - :height 1080} + ]]])) - {:name "PRINT (96dpi)"} - {:name "A0" - :width 3179 - :height 4494} - {:name "A1" - :width 2245 - :height 3179} - {:name "A2" - :width 1587 - :height 2245} - {:name "A3" - :width 1123 - :height 1587} - {:name "A4" - :width 794 - :height 1123} - {:name "A5" - :width 559 - :height 794} - {:name "A6" - :width 397 - :height 559} - {:name "Letter" - :width 816 - :height 1054} - {:name "DIN Lang" - :width 835 - :height 413} - {:name "SOCIAL MEDIA"} - {:name "Instagram profile" - :width 320 - :height 320} - {:name "Instagram post" - :width 1080 - :height 1080} - {:name "Instagram story" - :width 1080 - :height 1920} - {:name "Facebook profile" - :width 720 - :height 720} - {:name "Facebook cover" - :width 820 - :height 312} - {:name "Facebook post" - :width 1200 - :height 630} - {:name "LinkedIn profile" - :width 400 - :height 400} - {:name "LinkedIn cover" - :width 1584 - :height 396} - {:name "LinkedIn post" - :width 1200 - :height 627} - {:name "Twitter profile" - :width 400 - :height 400} - {:name "Twitter header" - :width 1500 - :height 500} - {:name "Twitter post" - :width 1024 - :height 512} - {:name "YouTube profile" - :width 800 - :height 800} - {:name "YouTube banner" - :width 2560 - :height 1440} - {:name "YouTube thumb" - :width 1280 - :height 720}]) diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 80c600da1..dd4123d48 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -172,6 +172,7 @@ over-shapes-stream (mf/deps page-id objects) (fn [ids] + #_(prn "??hover-ids" (->> ids (map #(get-in objects [% :name])))) (let [is-group? (fn [id] (contains? #{:group :bool} (get-in objects [id :type]))) @@ -221,7 +222,7 @@ [objects hover-ids selected active-frames zoom transform vbox] (let [frame? #(= :frame (get-in objects [% :type])) - all-frames (mf/use-memo (mf/deps objects) #(cph/get-frames-ids objects)) + all-frames (mf/use-memo (mf/deps objects) #(cph/get-root-frames-ids objects)) selected-frames (mf/use-memo (mf/deps selected) #(->> all-frames (filter selected))) xf-selected-frame (comp (remove frame?) (map #(get-in objects [% :frame-id]))) selected-shapes-frames (mf/use-memo (mf/deps selected) #(into #{} xf-selected-frame selected)) diff --git a/frontend/src/app/main/ui/workspace/viewport/selection.cljs b/frontend/src/app/main/ui/workspace/viewport/selection.cljs index dcac1a492..7b1a5ebd3 100644 --- a/frontend/src/app/main/ui/workspace/viewport/selection.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/selection.cljs @@ -316,7 +316,7 @@ :color color} props (map->obj (merge common-props props))] (case type - :rotation (when (not= :frame (:type shape)) [:> rotation-handler props]) + :rotation [:> rotation-handler props] :resize-point [:> resize-point-handler props] :resize-side [:> resize-side-handler props])))]))) diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index 67c58faa9..e62258148 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.workspace.viewport.widgets (:require + [app.common.uuid :as uuid] [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] @@ -182,15 +183,16 @@ [:g.frame-titles (for [frame frames] - [:& frame-title {:key (dm/str "frame-title-" (:id frame)) - :frame frame - :selected? (contains? selected (:id frame)) - :zoom zoom - :show-artboard-names? show-artboard-names? - :modifiers modifiers - :on-frame-enter on-frame-enter - :on-frame-leave on-frame-leave - :on-frame-select on-frame-select}])])) + (when (= (:frame-id frame) uuid/zero) + [:& frame-title {:key (dm/str "frame-title-" (:id frame)) + :frame frame + :selected? (contains? selected (:id frame)) + :zoom zoom + :show-artboard-names? show-artboard-names? + :modifiers modifiers + :on-frame-enter on-frame-enter + :on-frame-leave on-frame-leave + :on-frame-select on-frame-select}]))])) (mf/defc frame-flow [{:keys [flow frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}] diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index 692cf61ce..dddee6f55 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -86,12 +86,17 @@ index (reduce index-shape initial-quadtree shapes) - z-index (cp/calculate-z-index objects)] + ;;z-index (cp/calculate-z-index objects) + ] - {:index index :z-index z-index :bounds bounds})) + {:index index + ;;:z-index z-index + :bounds bounds})) (defn- update-index - [{index :index z-index :z-index :as data} old-objects new-objects] + [{index :index + ;; z-index :z-index + :as data} old-objects new-objects] (let [changes? (fn [id] (not= (get old-objects id) (get new-objects id))) @@ -112,12 +117,16 @@ index-shape (make-index-shape new-objects parents-index clip-parents-index) index (reduce index-shape new-index shapes) - z-index (cp/update-z-index z-index changed-ids old-objects new-objects)] + ;;z-index (cp/update-z-index z-index changed-ids old-objects new-objects) + ] - (assoc data :index index :z-index z-index))) + (assoc data :index index ;;:z-index z-index + ))) (defn- query-index - [{index :index z-index :z-index} rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?] + [{index :index + ;;z-index :z-index + } rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?] (let [result (-> (qdt/search index (clj->js rect)) (es6-iterator-seq)) @@ -145,10 +154,10 @@ (fn [clip-parents] (->> clip-parents (some (comp not overlaps?)) not)) - add-z-index - (fn [{:keys [id frame-id] :as shape}] - (assoc shape :z (+ (get z-index id) - (get z-index frame-id 0)))) + ;;add-z-index + ;;(fn [{:keys [id frame-id] :as shape}] + ;; (assoc shape :z (+ (get z-index id) + ;; (get z-index frame-id 0)))) ;; Shapes after filters of overlapping and criteria matching-shapes @@ -160,14 +169,15 @@ (filter (if clip-children? (comp overlaps-parent? :clip-parents) (constantly true))) - (map add-z-index)) + #_(map add-z-index)) result) - keyfn (if reverse? (comp - :z) :z)] + ;;keyfn (if reverse? (comp - :z) :z) + ] (into (d/ordered-set) (->> matching-shapes - (sort-by keyfn) + #_(sort-by keyfn) (map :id))))) @@ -208,7 +218,7 @@ (when-let [index (get @state page-id)] (query-index index rect frame-id full-frame? include-frames? ignore-groups? clip-children? reverse?))) -(defmethod impl/handler :selection/query-z-index +#_(defmethod impl/handler :selection/query-z-index [{:keys [page-id objects ids]}] (when-let [{z-index :z-index} (get @state page-id)] (->> ids (map #(+ (get z-index %) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 156b7ffc7..67d209c24 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -3890,6 +3890,12 @@ msgstr "X" msgid "workspace.options.y" msgstr "Y" +msgid "workspace.options.clip-content" +msgstr "Clip content" + +msgid"workspace.options.show-in-viewer" +msgstr "Show in view mode" + msgid "workspace.path.actions.add-node" msgstr "Add node (%s)" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 7d521607a..b6ec5136c 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -4058,6 +4058,12 @@ msgstr "X" msgid "workspace.options.y" msgstr "Y" +msgid "workspace.options.clip-content" +msgstr "Truncar contenido" + +msgid"workspace.options.show-in-viewer" +msgstr "Mostrar en modo visualización" + msgid "workspace.path.actions.add-node" msgstr "Añadir nodo (%s)"