diff --git a/backend/src/app/services/mutations/files.clj b/backend/src/app/services/mutations/files.clj index ff1d24396..17742805d 100644 --- a/backend/src/app/services/mutations/files.clj +++ b/backend/src/app/services/mutations/files.clj @@ -11,7 +11,7 @@ (:require [app.common.exceptions :as ex] [app.common.pages :as cp] - [app.common.pages-migrations :as pmg] + [app.common.pages.migrations :as pmg] [app.common.spec :as us] [app.common.uuid :as uuid] [app.config :as cfg] diff --git a/backend/src/app/services/queries/files.clj b/backend/src/app/services/queries/files.clj index 73dddf076..82393375e 100644 --- a/backend/src/app/services/queries/files.clj +++ b/backend/src/app/services/queries/files.clj @@ -10,7 +10,7 @@ (ns app.services.queries.files (:require [app.common.exceptions :as ex] - [app.common.pages-migrations :as pmg] + [app.common.pages.migrations :as pmg] [app.common.spec :as us] [app.db :as db] [app.services.queries :as sq] diff --git a/backend/src/app/services/queries/profile.clj b/backend/src/app/services/queries/profile.clj index db954905d..9f73e7c88 100644 --- a/backend/src/app/services/queries/profile.clj +++ b/backend/src/app/services/queries/profile.clj @@ -23,7 +23,6 @@ (s/def ::email ::us/email) (s/def ::fullname ::us/string) -(s/def ::metadata any?) (s/def ::old-password ::us/string) (s/def ::password ::us/string) (s/def ::path ::us/string) diff --git a/backend/src/app/tasks/trim_file.clj b/backend/src/app/tasks/trim_file.clj index dd16f2fd0..56e81777c 100644 --- a/backend/src/app/tasks/trim_file.clj +++ b/backend/src/app/tasks/trim_file.clj @@ -9,7 +9,7 @@ (ns app.tasks.trim-file (:require - [app.common.pages-migrations :as pmg] + [app.common.pages.migrations :as pmg] [app.config :as cfg] [app.db :as db] [app.tasks :as tasks] diff --git a/common/app/common/geom/align.cljc b/common/app/common/geom/align.cljc index 4cec0fdf5..f06bcb4bd 100644 --- a/common/app/common/geom/align.cljc +++ b/common/app/common/geom/align.cljc @@ -19,7 +19,7 @@ (declare calc-align-pos) -;; Duplicated from pages-helpers to remove cyclic dependencies +;; Duplicated from pages/helpers to remove cyclic dependencies (defn- get-children [id objects] (let [shapes (vec (get-in objects [id :shapes]))] (if shapes diff --git a/common/app/common/geom/shapes.cljc b/common/app/common/geom/shapes.cljc index 628dc4322..1ddb96bb4 100644 --- a/common/app/common/geom/shapes.cljc +++ b/common/app/common/geom/shapes.cljc @@ -254,6 +254,15 @@ (assoc :selrect selrect :points points)))) +(defn rotation-modifiers + [center shape angle] + (let [displacement (let [shape-center (gco/center-shape shape)] + (-> (gmt/matrix) + (gmt/rotate angle center) + (gmt/rotate (- angle) shape-center)))] + {:rotation angle + :displacement displacement})) + ;; EXPORTS (d/export gco/center-shape) diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index cdec3e8df..c2ad88592 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -10,1141 +10,73 @@ (ns app.common.pages "A common (clj/cljs) functions and specs for pages." (:require - [clojure.spec.alpha :as s] [app.common.data :as d] - [app.common.pages-helpers :as cph] - [app.common.exceptions :as ex] - [app.common.geom.shapes :as gsh] - [app.common.geom.matrix :as gmt] - [app.common.geom.point :as gpt] - [app.common.spec :as us] - [app.common.uuid :as uuid])) - -(def file-version 4) -(def max-safe-int 9007199254740991) -(def min-safe-int -9007199254740991) - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Page Transformation Changes -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; --- Specs - -(s/def ::frame-id uuid?) -(s/def ::id uuid?) -(s/def ::name string?) -(s/def ::page-id uuid?) -(s/def ::parent-id uuid?) -(s/def ::string string?) -(s/def ::type keyword?) -(s/def ::uuid uuid?) - -(s/def ::component-id uuid?) -(s/def ::component-file uuid?) -(s/def ::component-root? boolean?) -(s/def ::shape-ref uuid?) - -(s/def ::safe-integer ::us/safe-integer) -(s/def ::safe-number ::us/safe-number) - -(s/def :internal.matrix/a ::us/safe-number) -(s/def :internal.matrix/b ::us/safe-number) -(s/def :internal.matrix/c ::us/safe-number) -(s/def :internal.matrix/d ::us/safe-number) -(s/def :internal.matrix/e ::us/safe-number) -(s/def :internal.matrix/f ::us/safe-number) - -(s/def ::matrix - (s/and (s/keys :req-un [:internal.matrix/a - :internal.matrix/b - :internal.matrix/c - :internal.matrix/d - :internal.matrix/e - :internal.matrix/f]) - gmt/matrix?)) - - -(s/def :internal.point/x ::us/safe-number) -(s/def :internal.point/y ::us/safe-number) - -(s/def ::point - (s/and (s/keys :req-un [:internal.point/x - :internal.point/y]) - gpt/point?)) - -;; GRADIENTS - -(s/def :internal.gradient.stop/color ::string) -(s/def :internal.gradient.stop/opacity ::safe-number) -(s/def :internal.gradient.stop/offset ::safe-number) - -(s/def :internal.gradient/type #{:linear :radial}) -(s/def :internal.gradient/start-x ::safe-number) -(s/def :internal.gradient/start-y ::safe-number) -(s/def :internal.gradient/end-x ::safe-number) -(s/def :internal.gradient/end-y ::safe-number) -(s/def :internal.gradient/width ::safe-number) - -(s/def :internal.gradient/stop - (s/keys :req-un [:internal.gradient.stop/color - :internal.gradient.stop/opacity - :internal.gradient.stop/offset])) - -(s/def :internal.gradient/stops - (s/coll-of :internal.gradient/stop :kind vector?)) - -(s/def ::gradient - (s/keys :req-un [:internal.gradient/type - :internal.gradient/start-x - :internal.gradient/start-y - :internal.gradient/end-x - :internal.gradient/end-y - :internal.gradient/width - :internal.gradient/stops])) - - -;;; COLORS - -(s/def :internal.color/name ::string) -(s/def :internal.color/value (s/nilable ::string)) -(s/def :internal.color/color (s/nilable ::string)) -(s/def :internal.color/opacity (s/nilable ::safe-number)) -(s/def :internal.color/gradient (s/nilable ::gradient)) - -(s/def ::color - (s/keys :opt-un [::id - :internal.color/name - :internal.color/value - :internal.color/color - :internal.color/opacity - :internal.color/gradient])) - - - -;;; SHADOW EFFECT - -(s/def :internal.shadow/id uuid?) -(s/def :internal.shadow/style #{:drop-shadow :inner-shadow}) -(s/def :internal.shadow/color ::color) -(s/def :internal.shadow/offset-x ::safe-number) -(s/def :internal.shadow/offset-y ::safe-number) -(s/def :internal.shadow/blur ::safe-number) -(s/def :internal.shadow/spread ::safe-number) -(s/def :internal.shadow/hidden boolean?) - -(s/def :internal.shadow/shadow - (s/keys :req-un [:internal.shadow/id - :internal.shadow/style - :internal.shadow/color - :internal.shadow/offset-x - :internal.shadow/offset-y - :internal.shadow/blur - :internal.shadow/spread - :internal.shadow/hidden])) - -(s/def ::shadow - (s/coll-of :internal.shadow/shadow :kind vector?)) - - -;;; BLUR EFFECT - -(s/def :internal.blur/id uuid?) -(s/def :internal.blur/type #{:layer-blur}) -(s/def :internal.blur/value ::safe-number) -(s/def :internal.blur/hidden boolean?) - -(s/def ::blur - (s/keys :req-un [:internal.blur/id - :internal.blur/type - :internal.blur/value - :internal.blur/hidden])) - -;; Page Options -(s/def :internal.page.grid.color/value string?) -(s/def :internal.page.grid.color/opacity ::safe-number) - -(s/def :internal.page.grid/size ::safe-integer) -(s/def :internal.page.grid/color - (s/keys :req-un [:internal.page.grid.color/value - :internal.page.grid.color/opacity])) - -(s/def :internal.page.grid/type #{:stretch :left :center :right}) -(s/def :internal.page.grid/item-length (s/nilable ::safe-integer)) -(s/def :internal.page.grid/gutter (s/nilable ::safe-integer)) -(s/def :internal.page.grid/margin (s/nilable ::safe-integer)) - -(s/def :internal.page.grid/square - (s/keys :req-un [:internal.page.grid/size - :internal.page.grid/color])) - -(s/def :internal.page.grid/column - (s/keys :req-un [:internal.page.grid/size - :internal.page.grid/color - :internal.page.grid/type - :internal.page.grid/item-length - :internal.page.grid/gutter - :internal.page.grid/margin])) - -(s/def :internal.page.grid/row :internal.page.grid/column) - -(s/def :internal.page.options/background string?) -(s/def :internal.page.options/saved-grids - (s/keys :req-un [:internal.page.grid/square - :internal.page.grid/row - :internal.page.grid/column])) - -(s/def :internal.page/options - (s/keys :opt-un [:internal.page.options/background])) - -;; Interactions - -(s/def :internal.shape.interaction/event-type #{:click}) ; In the future we will have more options -(s/def :internal.shape.interaction/action-type #{:navigate}) -(s/def :internal.shape.interaction/destination ::uuid) - -(s/def :internal.shape/interaction - (s/keys :req-un [:internal.shape.interaction/event-type - :internal.shape.interaction/action-type - :internal.shape.interaction/destination])) - -(s/def :internal.shape/interactions - (s/coll-of :internal.shape/interaction :kind vector?)) - -;; Page Data related -(s/def :internal.shape/blocked boolean?) -(s/def :internal.shape/collapsed boolean?) -(s/def :internal.shape/content any?) - -(s/def :internal.shape/fill-color string?) -(s/def :internal.shape/fill-opacity ::safe-number) -(s/def :internal.shape/fill-color-gradient (s/nilable ::gradient)) -(s/def :internal.shape/fill-color-ref-file (s/nilable uuid?)) -(s/def :internal.shape/fill-color-ref-id (s/nilable uuid?)) - -(s/def :internal.shape/font-family string?) -(s/def :internal.shape/font-size ::safe-integer) -(s/def :internal.shape/font-style string?) -(s/def :internal.shape/font-weight string?) -(s/def :internal.shape/hidden boolean?) -(s/def :internal.shape/letter-spacing ::safe-number) -(s/def :internal.shape/line-height ::safe-number) -(s/def :internal.shape/locked boolean?) -(s/def :internal.shape/page-id uuid?) -(s/def :internal.shape/proportion ::safe-number) -(s/def :internal.shape/proportion-lock boolean?) -(s/def :internal.shape/rx ::safe-number) -(s/def :internal.shape/ry ::safe-number) -(s/def :internal.shape/stroke-color string?) -(s/def :internal.shape/stroke-color-gradient (s/nilable ::gradient)) -(s/def :internal.shape/stroke-color-ref-file (s/nilable uuid?)) -(s/def :internal.shape/stroke-color-ref-id (s/nilable uuid?)) -(s/def :internal.shape/stroke-opacity ::safe-number) -(s/def :internal.shape/stroke-style #{:solid :dotted :dashed :mixed :none}) -(s/def :internal.shape/stroke-width ::safe-number) -(s/def :internal.shape/stroke-alignment #{:center :inner :outer}) -(s/def :internal.shape/text-align #{"left" "right" "center" "justify"}) -(s/def :internal.shape/x ::safe-number) -(s/def :internal.shape/y ::safe-number) -(s/def :internal.shape/cx ::safe-number) -(s/def :internal.shape/cy ::safe-number) -(s/def :internal.shape/width ::safe-number) -(s/def :internal.shape/height ::safe-number) -(s/def :internal.shape/index integer?) -(s/def :internal.shape/shadow ::shadow) -(s/def :internal.shape/blur ::blur) - -(s/def :internal.shape/x1 ::safe-number) -(s/def :internal.shape/y1 ::safe-number) -(s/def :internal.shape/x2 ::safe-number) -(s/def :internal.shape/y2 ::safe-number) - -(s/def :internal.shape.export/suffix string?) -(s/def :internal.shape.export/scale ::safe-number) -(s/def :internal.shape/export - (s/keys :req-un [::type - :internal.shape.export/suffix - :internal.shape.export/scale])) - -(s/def :internal.shape/exports - (s/coll-of :internal.shape/export :kind vector?)) - -(s/def :internal.shape/selrect - (s/keys :req-un [:internal.shape/x - :internal.shape/y - :internal.shape/x1 - :internal.shape/y1 - :internal.shape/x2 - :internal.shape/y2 - :internal.shape/width - :internal.shape/height])) - -(s/def :internal.shape/points - (s/every ::point :kind vector?)) - -(s/def :internal.shape/shapes - (s/every uuid? :kind vector?)) - -(s/def :internal.shape/transform ::matrix) -(s/def :internal.shape/transform-inverse ::matrix) - -(s/def ::shape-attrs - (s/keys :opt-un [:internal.shape/selrect - :internal.shape/points - :internal.shape/blocked - :internal.shape/collapsed - :internal.shape/content - :internal.shape/fill-color - :internal.shape/fill-opacity - :internal.shape/fill-color-gradient - :internal.shape/fill-color-ref-file - :internal.shape/fill-color-ref-id - :internal.shape/font-family - :internal.shape/font-size - :internal.shape/font-style - :internal.shape/font-weight - :internal.shape/hidden - :internal.shape/letter-spacing - :internal.shape/line-height - :internal.shape/locked - :internal.shape/proportion - :internal.shape/proportion-lock - :internal.shape/rx - :internal.shape/ry - :internal.shape/x - :internal.shape/y - :internal.shape/exports - :internal.shape/shapes - :internal.shape/stroke-color - :internal.shape/stroke-color-ref-file - :internal.shape/stroke-color-ref-id - :internal.shape/stroke-opacity - :internal.shape/stroke-style - :internal.shape/stroke-width - :internal.shape/stroke-alignment - :internal.shape/text-align - :internal.shape/transform - :internal.shape/transform-inverse - :internal.shape/width - :internal.shape/height - :internal.shape/interactions - :internal.shape/masked-group? - :internal.shape/shadow - :internal.shape/blur])) - -(def component-sync-attrs {:fill-color :fill-group - :fill-opacity :fill-group - :fill-color-gradient :fill-group - :fill-color-ref-file :fill-group - :fill-color-ref-id :fill-group - :content :content-group - :font-family :text-font-group - :font-size :text-font-group - :font-style :text-font-group - :font-weight :text-font-group - :letter-spacing :text-display-group - :line-height :text-display-group - :text-align :text-display-group - :stroke-color :stroke-group - :stroke-color-gradient :stroke-group - :stroke-color-ref-file :stroke-group - :stroke-color-ref-id :stroke-group - :stroke-opacity :stroke-group - :stroke-style :stroke-group - :stroke-width :stroke-group - :stroke-alignment :stroke-group - :rx :radius-group - :ry :radius-group - :selrect :geometry-group - :points :geometry-group - :locked :geometry-group - :proportion :geometry-group - :proportion-lock :geometry-group - :x :geometry-group - :y :geometry-group - :width :geometry-group - :height :geometry-group - :transform :geometry-group - :transform-inverse :geometry-group - :shadow :shadow-group - :blur :blur-group - :masked-group? :mask-group}) - ;; shapes-group is handled differently - -(s/def ::minimal-shape - (s/keys :req-un [::type ::name] - :opt-un [::id])) - -(s/def ::shape - (s/and ::minimal-shape ::shape-attrs - (s/keys :opt-un [::id - ::component-id - ::component-file - ::component-root? - ::shape-ref]))) - -(s/def :internal.page/objects (s/map-of uuid? ::shape)) - -(s/def ::page - (s/keys :req-un [::id - ::name - :internal.page/options - :internal.page/objects])) - - -(s/def ::recent-color - (s/keys :opt-un [:internal.color/value - :internal.color/color - :internal.color/opacity - :internal.color/gradient])) - -(s/def :internal.media-object/name ::string) -(s/def :internal.media-object/path ::string) -(s/def :internal.media-object/width ::safe-integer) -(s/def :internal.media-object/height ::safe-integer) -(s/def :internal.media-object/mtype ::string) -(s/def :internal.media-object/thumb-path ::string) -(s/def :internal.media-object/thumb-width ::safe-integer) -(s/def :internal.media-object/thumb-height ::safe-integer) -(s/def :internal.media-object/thumb-mtype ::string) - -(s/def ::media-object - (s/keys :req-un [::id ::name - :internal.media-object/name - :internal.media-object/path - :internal.media-object/width - :internal.media-object/height - :internal.media-object/mtype - :internal.media-object/thumb-path])) - -(s/def ::media-object-update - (s/keys :req-un [::id] - :req-opt [::name - :internal.media-object/name - :internal.media-object/path - :internal.media-object/width - :internal.media-object/height - :internal.media-object/mtype - :internal.media-object/thumb-path])) - -(s/def :internal.file/colors - (s/map-of ::uuid ::color)) - -(s/def :internal.file/recent-colors - (s/coll-of ::recent-color :kind vector?)) - -(s/def :internal.typography/id ::id) -(s/def :internal.typography/name ::string) -(s/def :internal.typography/font-id ::string) -(s/def :internal.typography/font-family ::string) -(s/def :internal.typography/font-variant-id ::string) -(s/def :internal.typography/font-size ::string) -(s/def :internal.typography/font-weight ::string) -(s/def :internal.typography/font-style ::string) -(s/def :internal.typography/line-height ::string) -(s/def :internal.typography/letter-spacing ::string) -(s/def :internal.typography/text-transform ::string) - -(s/def ::typography - (s/keys :req-un [:internal.typography/id - :internal.typography/name - :internal.typography/font-id - :internal.typography/font-family - :internal.typography/font-variant-id - :internal.typography/font-size - :internal.typography/font-weight - :internal.typography/font-style - :internal.typography/line-height - :internal.typography/letter-spacing - :internal.typography/text-transform])) - -(s/def :internal.file/pages - (s/coll-of ::uuid :kind vector?)) - -(s/def :internal.file/media - (s/map-of ::uuid ::media-object)) - -(s/def :internal.file/pages-index - (s/map-of ::uuid ::page)) - -(s/def ::data - (s/keys :req-un [:internal.file/pages-index - :internal.file/pages] - :opt-un [:internal.file/colors - :internal.file/recent-colors - :internal.file/media])) - -(s/def :internal.container/type #{:page :component}) - -(s/def ::container - (s/keys :req-un [:internal.container/type - ::id - ::name - :internal.page/objects])) - -(defmulti operation-spec :type) - -(s/def :internal.operations.set/attr keyword?) -(s/def :internal.operations.set/val any?) -(s/def :internal.operations.set/touched - (s/nilable (s/every keyword? :kind set?))) - -(defmethod operation-spec :set [_] - (s/keys :req-un [:internal.operations.set/attr - :internal.operations.set/val])) - -(defmethod operation-spec :set-touched [_] - (s/keys :req-un [:internal.operations.set/touched])) - -(defmulti change-spec :type) - -(s/def :internal.changes.set-option/option any?) -(s/def :internal.changes.set-option/value any?) - -(defmethod change-spec :set-option [_] - (s/keys :req-un [:internal.changes.set-option/option - :internal.changes.set-option/value])) - -(s/def :internal.changes.add-obj/obj ::shape) - -(defn- valid-container-id-frame? - [o] - (or (and (contains? o :page-id) - (not (contains? o :component-id)) - (some? (:frame-id o))) - (and (contains? o :component-id) - (not (contains? o :page-id)) - (nil? (:frame-id o))))) - -(defn- valid-container-id? - [o] - (or (and (contains? o :page-id) - (not (contains? o :component-id))) - (and (contains? o :component-id) - (not (contains? o :page-id))))) - -(defmethod change-spec :add-obj [_] - (s/and (s/keys :req-un [::id :internal.changes.add-obj/obj] - :opt-un [::page-id ::component-id ::parent-id ::frame-id]) - valid-container-id-frame?)) - -(s/def ::operation (s/multi-spec operation-spec :type)) -(s/def ::operations (s/coll-of ::operation)) - -(defmethod change-spec :mod-obj [_] - (s/and (s/keys :req-un [::id ::operations] - :opt-un [::page-id ::component-id]) - valid-container-id?)) - -(defmethod change-spec :del-obj [_] - (s/and (s/keys :req-un [::id] - :opt-un [::page-id ::component-id]) - valid-container-id?)) - -(s/def :internal.changes.reg-objects/shapes - (s/coll-of uuid? :kind vector?)) - -(defmethod change-spec :reg-objects [_] - (s/and (s/keys :req-un [:internal.changes.reg-objects/shapes] - :opt-un [::page-id ::component-id]) - valid-container-id?)) - -(defmethod change-spec :mov-objects [_] - (s/and (s/keys :req-un [::parent-id :internal.shape/shapes] - :opt-un [::page-id ::component-id ::index]) - valid-container-id?)) - -(defmethod change-spec :add-page [_] - (s/or :empty (s/keys :req-un [::id ::name]) - :complete (s/keys :req-un [::page]))) - -(defmethod change-spec :mod-page [_] - (s/keys :req-un [::id ::name])) - -(defmethod change-spec :del-page [_] - (s/keys :req-un [::id])) - -(defmethod change-spec :mov-page [_] - (s/keys :req-un [::id ::index])) - -(defmethod change-spec :add-color [_] - (s/keys :req-un [::color])) - -(defmethod change-spec :mod-color [_] - (s/keys :req-un [::color])) - -(defmethod change-spec :del-color [_] - (s/keys :req-un [::id])) - -(s/def :internal.changes.add-recent-color/color ::recent-color) - -(defmethod change-spec :add-recent-color [_] - (s/keys :req-un [:internal.changes.add-recent-color/color])) - -(s/def :internal.changes.media/object ::media-object) - -(defmethod change-spec :add-media [_] - (s/keys :req-un [:internal.changes.media/object])) - -(s/def :internal.changes.media.mod/object ::media-object-update) - -(defmethod change-spec :mod-media [_] - (s/keys :req-un [:internal.changes.media.mod/object])) - -(defmethod change-spec :del-media [_] - (s/keys :req-un [::id])) - -(s/def :internal.changes.add-component/shapes - (s/coll-of ::shape)) - -(defmethod change-spec :add-component [_] - (s/keys :req-un [::id ::name :internal.changes.add-component/shapes])) - -(defmethod change-spec :mod-component [_] - (s/keys :req-un [::id] - :opt-un [::name :internal.changes.add-component/shapes])) - -(defmethod change-spec :del-component [_] - (s/keys :req-un [::id])) - -(s/def :internal.changes.typography/typography ::typography) - -(defmethod change-spec :add-typography [_] - (s/keys :req-un [:internal.changes.typography/typography])) - -(defmethod change-spec :mod-typography [_] - (s/keys :req-un [:internal.changes.typography/typography])) - -(defmethod change-spec :del-typography [_] - (s/keys :req-un [:internal.typography/id])) - -(s/def ::change (s/multi-spec change-spec :type)) -(s/def ::changes (s/coll-of ::change)) - -(def root uuid/zero) - -(def empty-page-data - {:options {} - :name "Page" - :objects - {root - {:id root - :type :frame - :name "Root Frame"}}}) - -(def empty-file-data - {:version file-version - :pages [] - :pages-index {}}) - -(def default-color "#b1b2b5") ;; $color-gray-20 -(def default-shape-attrs - {:fill-color default-color - :fill-opacity 1}) - -(def default-frame-attrs - {:frame-id uuid/zero - :fill-color "#ffffff" - :fill-opacity 1 - :shapes []}) - -(def ^:private minimal-shapes - [{:type :rect - :name "Rect" - :fill-color default-color - :fill-opacity 1 - :stroke-style :none - :stroke-alignment :center - :stroke-width 0 - :stroke-color "#000000" - :stroke-opacity 0 - :rx 0 - :ry 0} - - {:type :image} - - {:type :icon} - - {:type :circle - :name "Circle" - :fill-color default-color - :fill-opacity 1 - :stroke-style :none - :stroke-alignment :center - :stroke-width 0 - :stroke-color "#000000" - :stroke-opacity 0} - - {:type :path - :name "Path" - :fill-color "#000000" - :fill-opacity 0 - :stroke-style :solid - :stroke-alignment :center - :stroke-width 2 - :stroke-color "#000000" - :stroke-opacity 1} - - {:type :frame - :name "Artboard" - :fill-color "#ffffff" - :fill-opacity 1 - :stroke-style :none - :stroke-alignment :center - :stroke-width 0 - :stroke-color "#000000" - :stroke-opacity 0} - - {:type :text - :name "Text" - :content nil}]) - -(defn make-minimal-shape - [type] - (let [type (cond (= type :curve) :path - :else type) - shape (d/seek #(= type (:type %)) minimal-shapes)] - (when-not shape - (ex/raise :type :assertion - :code :shape-type-not-implemented - :context {:type type})) - - (cond-> shape - :always - (assoc :id (uuid/next)) - - (not= :path (:type shape)) - (assoc :x 0 - :y 0 - :width 1 - :height 1 - :selrect {:x 0 - :y 0 - :x1 0 - :y1 0 - :x2 1 - :y2 1 - :width 1 - :height 1})))) - -(defn make-minimal-group - [frame-id selection-rect group-name] - {:id (uuid/next) - :type :group - :name group-name - :shapes [] - :frame-id frame-id - :x (:x selection-rect) - :y (:y selection-rect) - :width (:width selection-rect) - :height (:height selection-rect)}) - -(defn make-file-data - ([] (make-file-data (uuid/next))) - ([id] - (let [ - pd (assoc empty-page-data - :id id - :name "Page-1")] - (-> empty-file-data - (update :pages conj id) - (update :pages-index assoc id pd))))) - -;; --- Changes Processing Impl - -(defmulti process-change (fn [_ change] (:type change))) -(defmulti process-operation (fn [_ op] (:type op))) - -(defn process-changes - [data items] - (->> (us/verify ::changes items) - (reduce #(do - ;; (prn "process-change" (:type %2) (:id %2)) - (or (process-change %1 %2) %1)) - data))) - -(defmethod process-change :set-option - [data {:keys [page-id option value]}] - (d/update-in-when data [:pages-index page-id] - (fn [data] - (let [path (if (seqable? option) option [option])] - (if value - (assoc-in data (into [:options] path) value) - (assoc data :options (d/dissoc-in (:options data) path))))))) - -(defmethod process-change :add-obj - [data {:keys [id obj page-id component-id frame-id parent-id - index ignore-touched]}] - (letfn [(update-fn [data] - (let [parent-id (or parent-id frame-id) - objects (:objects data) - obj (assoc obj - :frame-id frame-id - :parent-id parent-id - :id id)] - (if (and (or (nil? parent-id) (contains? objects parent-id)) - (or (nil? frame-id) (contains? objects frame-id))) - (-> data - (update :objects assoc id obj) - (update-in [:objects parent-id :shapes] - (fn [shapes] - (let [shapes (or shapes [])] - (cond - (some #{id} shapes) - shapes - - (nil? index) - (if (= :frame (:type obj)) - (d/concat [id] shapes) - (conj shapes id)) - - :else - (cph/insert-at-index shapes index [id]))))) - - (cond-> (and (:shape-ref (get-in data [:objects parent-id])) - (not= parent-id frame-id) - (not ignore-touched)) - (update-in [:objects parent-id :touched] - cph/set-touched-group :shapes-group))) - data)))] - (if page-id - (d/update-in-when data [:pages-index page-id] update-fn) - (d/update-in-when data [:components component-id] update-fn)))) - -(defmethod process-change :mod-obj - [data {:keys [id page-id component-id operations]}] - (let [update-fn (fn [objects] - (if-let [obj (get objects id)] - (let [result (reduce process-operation obj operations)] - (us/verify ::shape result) - (assoc objects id result)) - objects))] - (if page-id - (d/update-in-when data [:pages-index page-id :objects] update-fn) - (d/update-in-when data [:components component-id :objects] update-fn)))) - -(defmethod process-change :del-obj - [data {:keys [page-id component-id id ignore-touched]}] - (letfn [(delete-object [objects id] - (if-let [target (get objects id)] - (let [parent-id (cph/get-parent id objects) - frame-id (:frame-id target) - parent (get objects parent-id) - objects (dissoc objects id)] - (cond-> objects - (and (not= parent-id frame-id) - (= :group (:type parent))) - (update-in [parent-id :shapes] (fn [s] (filterv #(not= % id) s))) - - (and (:shape-ref parent) (not ignore-touched)) - (update-in [parent-id :touched] cph/set-touched-group :shapes-group) - - (contains? objects frame-id) - (update-in [frame-id :shapes] (fn [s] (filterv #(not= % id) s))) - - (seq (:shapes target)) ; Recursive delete all - ; dependend objects - (as-> $ (reduce delete-object $ (:shapes target))))) - objects))] - (if page-id - (d/update-in-when data [:pages-index page-id :objects] delete-object id) - (d/update-in-when data [:components component-id :objects] delete-object id)))) - -(defn rotation-modifiers - [center shape angle] - (let [displacement (let [shape-center (gsh/center-shape shape)] - (-> (gmt/matrix) - (gmt/rotate angle center) - (gmt/rotate (- angle) shape-center)))] - {:rotation angle - :displacement displacement})) - -;; reg-objects operation "regenerates" the values for the parent groups -(defmethod process-change :reg-objects - [data {:keys [page-id component-id shapes]}] - (letfn [(reg-objects [objects] - (reduce #(update %1 %2 update-group %1) objects - (sequence (comp - (mapcat #(cons % (cph/get-parents % objects))) - (map #(get objects %)) - (filter #(= (:type %) :group)) - (map :id) - (distinct)) - shapes))) - (update-group [group objects] - (let [children (->> group :shapes (map #(get objects %)))] - (if (:masked-group? group) - (let [mask (first children)] - (-> group - (merge (select-keys mask [:selrect :points])) - (assoc :x (-> mask :selrect :x) - :y (-> mask :selrect :y) - :width (-> mask :selrect :width) - :height (-> mask :selrect :height)))) - (gsh/update-group-selrect group children))))] - - (if page-id - (d/update-in-when data [:pages-index page-id :objects] reg-objects) - (d/update-in-when data [:components component-id :objects] reg-objects)))) - -(defmethod process-change :mov-objects - [data {:keys [parent-id shapes index page-id component-id ignore-touched]}] - (letfn [(is-valid-move? [objects shape-id] - (let [invalid-targets (cph/calculate-invalid-targets shape-id objects)] - (and (not (invalid-targets parent-id)) - (cph/valid-frame-target shape-id parent-id objects)))) - - (insert-items [prev-shapes index shapes] - (let [prev-shapes (or prev-shapes [])] - (if index - (cph/insert-at-index prev-shapes index shapes) - (cph/append-at-the-end prev-shapes shapes)))) - - (check-insert-items [prev-shapes parent index shapes] - (if-not (:masked-group? parent) - (insert-items prev-shapes index shapes) - ;; For masked groups, the first shape is the mask - ;; and it cannot be moved. - (let [mask-id (first prev-shapes) - other-ids (rest prev-shapes) - not-mask-shapes (strip-id shapes mask-id) - new-index (if (nil? index) nil (max (dec index) 0)) - new-shapes (insert-items other-ids new-index not-mask-shapes)] - (d/concat [mask-id] new-shapes)))) - - (strip-id [coll id] - (filterv #(not= % id) coll)) - - (add-to-parent [parent index shapes] - (cond-> parent - true - (update :shapes check-insert-items parent index shapes) - - (and (:shape-ref parent) (= (:type parent) :group) (not ignore-touched)) - (update :touched cph/set-touched-group :shapes-group))) - - (remove-from-old-parent [cpindex objects shape-id] - (let [prev-parent-id (get cpindex shape-id)] - ;; Do nothing if the parent id of the shape is the same as - ;; the new destination target parent id. - (if (= prev-parent-id parent-id) - objects - (loop [sid shape-id - pid prev-parent-id - objects objects] - (let [obj (get objects pid)] - (if (and (= 1 (count (:shapes obj))) - (= sid (first (:shapes obj))) - (= :group (:type obj))) - (recur pid - (:parent-id obj) - (dissoc objects pid)) - (cond-> objects - true - (update-in [pid :shapes] strip-id sid) - - (and (:shape-ref obj) - (= (:type obj) :group) - (not ignore-touched)) - (update-in [pid :touched] - cph/set-touched-group :shapes-group)))))))) - - (update-parent-id [objects id] - (update objects id assoc :parent-id parent-id)) - - ;; Updates the frame-id references that might be outdated - (assign-frame-id [frame-id objects id] - (let [objects (update objects id assoc :frame-id frame-id) - obj (get objects id)] - (cond-> objects - ;; If we moving frame, the parent frame is the root - ;; and we DO NOT NEED update children because the - ;; children will point correctly to the frame what we - ;; are currently moving - (not= :frame (:type obj)) - (as-> $$ (reduce (partial assign-frame-id frame-id) $$ (:shapes obj)))))) - - (move-objects [objects] - (let [valid? (every? (partial is-valid-move? objects) shapes) - - ;; Create a index of shape ids pointing to the - ;; corresponding parents; used mainly for update old - ;; parents after move operation. - cpindex (reduce (fn [index id] - (let [obj (get objects id)] - (assoc! index id (:parent-id obj)))) - (transient {}) - (keys objects)) - cpindex (persistent! cpindex) - - parent (get objects parent-id) - frame-id (if (= :frame (:type parent)) - (:id parent) - (:frame-id parent))] - - (if (and valid? (seq shapes)) - (as-> objects $ - ;; Add the new shapes to the parent object. - (update $ parent-id #(add-to-parent % index shapes)) - - ;; Update each individual shapre link to the new parent - (reduce update-parent-id $ shapes) - - ;; Analyze the old parents and clear the old links - ;; only if the new parrent is different form old - ;; parent. - (reduce (partial remove-from-old-parent cpindex) $ shapes) - - ;; Ensure that all shapes of the new parent has a - ;; correct link to the topside frame. - (reduce (partial assign-frame-id frame-id) $ shapes)) - objects)))] - - (if page-id - (d/update-in-when data [:pages-index page-id :objects] move-objects) - (d/update-in-when data [:components component-id :objects] move-objects)))) - -(defmethod process-change :add-page - [data {:keys [id name page]}] - (cond - (and (string? name) (uuid? id)) - (let [page (assoc empty-page-data - :id id - :name name)] - (-> data - (update :pages conj id) - (update :pages-index assoc id page))) - - (map? page) - (-> data - (update :pages conj (:id page)) - (update :pages-index assoc (:id page) page)) - - :else - (ex/raise :type :conflict - :hint "name or page should be provided, never both"))) - -(defmethod process-change :mod-page - [data {:keys [id name]}] - (d/update-in-when data [:pages-index id] assoc :name name)) - -(defmethod process-change :del-page - [data {:keys [id]}] - (-> data - (update :pages (fn [pages] (filterv #(not= % id) pages))) - (update :pages-index dissoc id))) - -(defmethod process-change :mov-page - [data {:keys [id index]}] - (update data :pages cph/insert-at-index index [id])) - -(defmethod process-change :add-color - [data {:keys [color]}] - (update data :colors assoc (:id color) color)) - -(defmethod process-change :mod-color - [data {:keys [color]}] - (d/assoc-in-when data [:colors (:id color)] color)) - -(defmethod process-change :del-color - [data {:keys [id]}] - (update data :colors dissoc id)) - -(defmethod process-change :add-recent-color - [data {:keys [color]}] - ;; Moves the color to the top of the list and then truncates up to 15 - (update data :recent-colors (fn [rc] - (let [rc (conj (filterv (comp not #{color}) (or rc [])) color)] - (if (> (count rc) 15) - (subvec rc 1) - rc))))) - -;; -- Media - -(defmethod process-change :add-media - [data {:keys [object]}] - (update data :media assoc (:id object) object)) - -(defmethod process-change :mod-media - [data {:keys [object]}] - (d/update-in-when data [:media (:id object)] merge object)) - -(defmethod process-change :del-media - [data {:keys [id]}] - (update data :media dissoc id)) - -;; -- Components - -(defmethod process-change :add-component - [data {:keys [id name shapes]}] - (assoc-in data [:components id] - {:id id - :name name - :objects (d/index-by :id shapes)})) - -(defmethod process-change :mod-component - [data {:keys [id name objects]}] - (update-in data [:components id] - #(cond-> % - (some? name) - (assoc :name name) - - (some? objects) - (assoc :objects objects)))) - -(defmethod process-change :del-component - [data {:keys [id]}] - (d/dissoc-in data [:components id])) - -;; -- Typography - -(defmethod process-change :add-typography - [data {:keys [typography]}] - (update data :typographies assoc (:id typography) typography)) - -(defmethod process-change :mod-typography - [data {:keys [typography]}] - (d/update-in-when data [:typographies (:id typography)] merge typography)) - -(defmethod process-change :del-typography - [data {:keys [id]}] - (update data :typographies dissoc id)) - -;; -- Operations - -(defmethod process-operation :set - [shape op] - (let [attr (:attr op) - val (:val op) - ignore (:ignore-touched op) - shape-ref (:shape-ref shape) - group (get component-sync-attrs attr)] - - (cond-> shape - (and shape-ref group (not ignore) (not= val (get shape attr)) - ;; FIXME: it's difficult to tell if the geometry changes affect - ;; an individual shape inside the component, or are for - ;; the whole component (in which case we shouldn't set - ;; touched). For the moment we disable geometry touched. - (not= group :geometry-group)) - (update :touched cph/set-touched-group group) - - (nil? val) - (dissoc attr) - - (some? val) - (assoc attr val)))) - -(defmethod process-operation :set-touched - [shape op] - (let [touched (:touched op) - shape-ref (:shape-ref shape)] - (if (or (nil? shape-ref) (nil? touched) (empty? touched)) - (dissoc shape :touched) - (assoc shape :touched touched)))) - -(defmethod process-operation :default - [_ op] - (ex/raise :type :not-implemented - :code :operation-not-implemented - :context {:type (:type op)})) - + [app.common.pages.changes :as changes] + [app.common.pages.common :as common] + [app.common.pages.helpers :as helpers] + [app.common.pages.init :as init] + [app.common.pages.spec :as spec] + [clojure.spec.alpha :as s])) + +;; Common +(d/export common/root) +(d/export common/file-version) +(d/export common/default-color) +(d/export common/component-sync-attrs) + +;; Helpers + +(d/export helpers/walk-pages) +(d/export helpers/select-objects) +(d/export helpers/update-object-list) +(d/export helpers/get-root-shape) +(d/export helpers/make-container) +(d/export helpers/page?) +(d/export helpers/component?) +(d/export helpers/get-container) +(d/export helpers/get-shape) +(d/export helpers/get-component) +(d/export helpers/is-master-of) +(d/export helpers/get-component-root) +(d/export helpers/get-children) +(d/export helpers/get-children-objects) +(d/export helpers/get-object-with-children) +(d/export helpers/is-shape-grouped) +(d/export helpers/get-parent) +(d/export helpers/get-parents) +(d/export helpers/generate-child-parent-index) +(d/export helpers/calculate-invalid-targets) +(d/export helpers/valid-frame-target) +(d/export helpers/position-on-parent) +(d/export helpers/insert-at-index) +(d/export helpers/append-at-the-end) +(d/export helpers/select-toplevel-shapes) +(d/export helpers/select-frames) +(d/export helpers/clone-object) +(d/export helpers/indexed-shapes) +(d/export helpers/expand-region-selection) +(d/export helpers/frame-id-by-position) +(d/export helpers/set-touched-group) +(d/export helpers/touched-group?) + +;; Process changes +(d/export changes/process-changes) + +;; Initialization +(d/export init/default-frame-attrs) +(d/export init/default-shape-attrs) +(d/export init/make-file-data) +(d/export init/make-minimal-shape) +(d/export init/make-minimal-group) + +;; Specs + +(s/def ::changes ::spec/changes) +(s/def ::color ::spec/color) +(s/def ::data ::spec/data) +(s/def ::media-object ::spec/media-object) +(s/def ::minimal-shape ::spec/minimal-shape) +(s/def ::page ::spec/page) +(s/def ::recent-color ::spec/recent-color) +(s/def ::shape-attrs ::spec/shape-attrs) +(s/def ::typography ::spec/typography) diff --git a/common/app/common/pages/changes.cljc b/common/app/common/pages/changes.cljc new file mode 100644 index 000000000..c327e0286 --- /dev/null +++ b/common/app/common/pages/changes.cljc @@ -0,0 +1,415 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.common.pages.changes + (:require + [app.common.data :as d] + [app.common.exceptions :as ex] + [app.common.geom.shapes :as gsh] + [app.common.pages.helpers :as cph] + [app.common.pages.spec :as ps] + [app.common.spec :as us] + [app.common.pages.common :refer [component-sync-attrs]] + [app.common.pages.init :as init] + [app.common.pages.spec :as spec])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Page Transformation Changes +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; --- Changes Processing Impl + +(defmulti process-change (fn [_ change] (:type change))) +(defmulti process-operation (fn [_ op] (:type op))) + +(defn process-changes + [data items] + (->> (us/verify ::spec/changes items) + (reduce #(do + ;; (prn "process-change" (:type %2) (:id %2)) + (or (process-change %1 %2) %1)) + data))) + +(defmethod process-change :set-option + [data {:keys [page-id option value]}] + (d/update-in-when data [:pages-index page-id] + (fn [data] + (let [path (if (seqable? option) option [option])] + (if value + (assoc-in data (into [:options] path) value) + (assoc data :options (d/dissoc-in (:options data) path))))))) + +(defmethod process-change :add-obj + [data {:keys [id obj page-id component-id frame-id parent-id + index ignore-touched]}] + (letfn [(update-fn [data] + (let [parent-id (or parent-id frame-id) + objects (:objects data) + obj (assoc obj + :frame-id frame-id + :parent-id parent-id + :id id)] + (if (and (or (nil? parent-id) (contains? objects parent-id)) + (or (nil? frame-id) (contains? objects frame-id))) + (-> data + (update :objects assoc id obj) + (update-in [:objects parent-id :shapes] + (fn [shapes] + (let [shapes (or shapes [])] + (cond + (some #{id} shapes) + shapes + + (nil? index) + (if (= :frame (:type obj)) + (d/concat [id] shapes) + (conj shapes id)) + + :else + (cph/insert-at-index shapes index [id]))))) + + (cond-> (and (:shape-ref (get-in data [:objects parent-id])) + (not= parent-id frame-id) + (not ignore-touched)) + (update-in [:objects parent-id :touched] + cph/set-touched-group :shapes-group))) + data)))] + (if page-id + (d/update-in-when data [:pages-index page-id] update-fn) + (d/update-in-when data [:components component-id] update-fn)))) + +(defmethod process-change :mod-obj + [data {:keys [id page-id component-id operations]}] + (let [update-fn (fn [objects] + (if-let [obj (get objects id)] + (let [result (reduce process-operation obj operations)] + (us/verify ::spec/shape result) + (assoc objects id result)) + objects))] + (if page-id + (d/update-in-when data [:pages-index page-id :objects] update-fn) + (d/update-in-when data [:components component-id :objects] update-fn)))) + +(defmethod process-change :del-obj + [data {:keys [page-id component-id id ignore-touched]}] + (letfn [(delete-object [objects id] + (if-let [target (get objects id)] + (let [parent-id (cph/get-parent id objects) + frame-id (:frame-id target) + parent (get objects parent-id) + objects (dissoc objects id)] + (cond-> objects + (and (not= parent-id frame-id) + (= :group (:type parent))) + (update-in [parent-id :shapes] (fn [s] (filterv #(not= % id) s))) + + (and (:shape-ref parent) (not ignore-touched)) + (update-in [parent-id :touched] cph/set-touched-group :shapes-group) + + (contains? objects frame-id) + (update-in [frame-id :shapes] (fn [s] (filterv #(not= % id) s))) + + (seq (:shapes target)) ; Recursive delete all + ; dependend objects + (as-> $ (reduce delete-object $ (:shapes target))))) + objects))] + (if page-id + (d/update-in-when data [:pages-index page-id :objects] delete-object id) + (d/update-in-when data [:components component-id :objects] delete-object id)))) + +;; reg-objects operation "regenerates" the values for the parent groups +(defmethod process-change :reg-objects + [data {:keys [page-id component-id shapes]}] + (letfn [(reg-objects [objects] + (reduce #(update %1 %2 update-group %1) objects + (sequence (comp + (mapcat #(cons % (cph/get-parents % objects))) + (map #(get objects %)) + (filter #(= (:type %) :group)) + (map :id) + (distinct)) + shapes))) + (update-group [group objects] + (let [children (->> group :shapes (map #(get objects %)))] + (if (:masked-group? group) + (let [mask (first children)] + (-> group + (merge (select-keys mask [:selrect :points])) + (assoc :x (-> mask :selrect :x) + :y (-> mask :selrect :y) + :width (-> mask :selrect :width) + :height (-> mask :selrect :height)))) + (gsh/update-group-selrect group children))))] + + (if page-id + (d/update-in-when data [:pages-index page-id :objects] reg-objects) + (d/update-in-when data [:components component-id :objects] reg-objects)))) + +(defmethod process-change :mov-objects + [data {:keys [parent-id shapes index page-id component-id ignore-touched]}] + (letfn [(is-valid-move? [objects shape-id] + (let [invalid-targets (cph/calculate-invalid-targets shape-id objects)] + (and (not (invalid-targets parent-id)) + (cph/valid-frame-target shape-id parent-id objects)))) + + (insert-items [prev-shapes index shapes] + (let [prev-shapes (or prev-shapes [])] + (if index + (cph/insert-at-index prev-shapes index shapes) + (cph/append-at-the-end prev-shapes shapes)))) + + (check-insert-items [prev-shapes parent index shapes] + (if-not (:masked-group? parent) + (insert-items prev-shapes index shapes) + ;; For masked groups, the first shape is the mask + ;; and it cannot be moved. + (let [mask-id (first prev-shapes) + other-ids (rest prev-shapes) + not-mask-shapes (strip-id shapes mask-id) + new-index (if (nil? index) nil (max (dec index) 0)) + new-shapes (insert-items other-ids new-index not-mask-shapes)] + (d/concat [mask-id] new-shapes)))) + + (strip-id [coll id] + (filterv #(not= % id) coll)) + + (add-to-parent [parent index shapes] + (cond-> parent + true + (update :shapes check-insert-items parent index shapes) + + (and (:shape-ref parent) (= (:type parent) :group) (not ignore-touched)) + (update :touched cph/set-touched-group :shapes-group))) + + (remove-from-old-parent [cpindex objects shape-id] + (let [prev-parent-id (get cpindex shape-id)] + ;; Do nothing if the parent id of the shape is the same as + ;; the new destination target parent id. + (if (= prev-parent-id parent-id) + objects + (loop [sid shape-id + pid prev-parent-id + objects objects] + (let [obj (get objects pid)] + (if (and (= 1 (count (:shapes obj))) + (= sid (first (:shapes obj))) + (= :group (:type obj))) + (recur pid + (:parent-id obj) + (dissoc objects pid)) + (cond-> objects + true + (update-in [pid :shapes] strip-id sid) + + (and (:shape-ref obj) + (= (:type obj) :group) + (not ignore-touched)) + (update-in [pid :touched] + cph/set-touched-group :shapes-group)))))))) + + (update-parent-id [objects id] + (update objects id assoc :parent-id parent-id)) + + ;; Updates the frame-id references that might be outdated + (assign-frame-id [frame-id objects id] + (let [objects (update objects id assoc :frame-id frame-id) + obj (get objects id)] + (cond-> objects + ;; If we moving frame, the parent frame is the root + ;; and we DO NOT NEED update children because the + ;; children will point correctly to the frame what we + ;; are currently moving + (not= :frame (:type obj)) + (as-> $$ (reduce (partial assign-frame-id frame-id) $$ (:shapes obj)))))) + + (move-objects [objects] + (let [valid? (every? (partial is-valid-move? objects) shapes) + + ;; Create a index of shape ids pointing to the + ;; corresponding parents; used mainly for update old + ;; parents after move operation. + cpindex (reduce (fn [index id] + (let [obj (get objects id)] + (assoc! index id (:parent-id obj)))) + (transient {}) + (keys objects)) + cpindex (persistent! cpindex) + + parent (get objects parent-id) + frame-id (if (= :frame (:type parent)) + (:id parent) + (:frame-id parent))] + + (if (and valid? (seq shapes)) + (as-> objects $ + ;; Add the new shapes to the parent object. + (update $ parent-id #(add-to-parent % index shapes)) + + ;; Update each individual shapre link to the new parent + (reduce update-parent-id $ shapes) + + ;; Analyze the old parents and clear the old links + ;; only if the new parrent is different form old + ;; parent. + (reduce (partial remove-from-old-parent cpindex) $ shapes) + + ;; Ensure that all shapes of the new parent has a + ;; correct link to the topside frame. + (reduce (partial assign-frame-id frame-id) $ shapes)) + objects)))] + + (if page-id + (d/update-in-when data [:pages-index page-id :objects] move-objects) + (d/update-in-when data [:components component-id :objects] move-objects)))) + +(defmethod process-change :add-page + [data {:keys [id name page]}] + (cond + (and (string? name) (uuid? id)) + (let [page (assoc init/empty-page-data + :id id + :name name)] + (-> data + (update :pages conj id) + (update :pages-index assoc id page))) + + (map? page) + (-> data + (update :pages conj (:id page)) + (update :pages-index assoc (:id page) page)) + + :else + (ex/raise :type :conflict + :hint "name or page should be provided, never both"))) + +(defmethod process-change :mod-page + [data {:keys [id name]}] + (d/update-in-when data [:pages-index id] assoc :name name)) + +(defmethod process-change :del-page + [data {:keys [id]}] + (-> data + (update :pages (fn [pages] (filterv #(not= % id) pages))) + (update :pages-index dissoc id))) + +(defmethod process-change :mov-page + [data {:keys [id index]}] + (update data :pages cph/insert-at-index index [id])) + +(defmethod process-change :add-color + [data {:keys [color]}] + (update data :colors assoc (:id color) color)) + +(defmethod process-change :mod-color + [data {:keys [color]}] + (d/assoc-in-when data [:colors (:id color)] color)) + +(defmethod process-change :del-color + [data {:keys [id]}] + (update data :colors dissoc id)) + +(defmethod process-change :add-recent-color + [data {:keys [color]}] + ;; Moves the color to the top of the list and then truncates up to 15 + (update data :recent-colors (fn [rc] + (let [rc (conj (filterv (comp not #{color}) (or rc [])) color)] + (if (> (count rc) 15) + (subvec rc 1) + rc))))) + +;; -- Media + +(defmethod process-change :add-media + [data {:keys [object]}] + (update data :media assoc (:id object) object)) + +(defmethod process-change :mod-media + [data {:keys [object]}] + (d/update-in-when data [:media (:id object)] merge object)) + +(defmethod process-change :del-media + [data {:keys [id]}] + (update data :media dissoc id)) + +;; -- Components + +(defmethod process-change :add-component + [data {:keys [id name shapes]}] + (assoc-in data [:components id] + {:id id + :name name + :objects (d/index-by :id shapes)})) + +(defmethod process-change :mod-component + [data {:keys [id name objects]}] + (update-in data [:components id] + #(cond-> % + (some? name) + (assoc :name name) + + (some? objects) + (assoc :objects objects)))) + +(defmethod process-change :del-component + [data {:keys [id]}] + (d/dissoc-in data [:components id])) + +;; -- Typography + +(defmethod process-change :add-typography + [data {:keys [typography]}] + (update data :typographies assoc (:id typography) typography)) + +(defmethod process-change :mod-typography + [data {:keys [typography]}] + (d/update-in-when data [:typographies (:id typography)] merge typography)) + +(defmethod process-change :del-typography + [data {:keys [id]}] + (update data :typographies dissoc id)) + +;; -- Operations + +(defmethod process-operation :set + [shape op] + (let [attr (:attr op) + val (:val op) + ignore (:ignore-touched op) + shape-ref (:shape-ref shape) + group (get component-sync-attrs attr)] + + (cond-> shape + (and shape-ref group (not ignore) (not= val (get shape attr)) + ;; FIXME: it's difficult to tell if the geometry changes affect + ;; an individual shape inside the component, or are for + ;; the whole component (in which case we shouldn't set + ;; touched). For the moment we disable geometry touched. + (not= group :geometry-group)) + (update :touched cph/set-touched-group group) + + (nil? val) + (dissoc attr) + + (some? val) + (assoc attr val)))) + +(defmethod process-operation :set-touched + [shape op] + (let [touched (:touched op) + shape-ref (:shape-ref shape)] + (if (or (nil? shape-ref) (nil? touched) (empty? touched)) + (dissoc shape :touched) + (assoc shape :touched touched)))) + +(defmethod process-operation :default + [_ op] + (ex/raise :type :not-implemented + :code :operation-not-implemented + :context {:type (:type op)})) + diff --git a/common/app/common/pages/common.cljc b/common/app/common/pages/common.cljc new file mode 100644 index 000000000..2d58b6bbe --- /dev/null +++ b/common/app/common/pages/common.cljc @@ -0,0 +1,56 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.common.pages.common + (:require + [app.common.uuid :as uuid])) + +(def file-version 4) +(def default-color "#b1b2b5") ;; $color-gray-20 +(def root uuid/zero) + +(def component-sync-attrs + {:fill-color :fill-group + :fill-opacity :fill-group + :fill-color-gradient :fill-group + :fill-color-ref-file :fill-group + :fill-color-ref-id :fill-group + :content :content-group + :font-family :text-font-group + :font-size :text-font-group + :font-style :text-font-group + :font-weight :text-font-group + :letter-spacing :text-display-group + :line-height :text-display-group + :text-align :text-display-group + :stroke-color :stroke-group + :stroke-color-gradient :stroke-group + :stroke-color-ref-file :stroke-group + :stroke-color-ref-id :stroke-group + :stroke-opacity :stroke-group + :stroke-style :stroke-group + :stroke-width :stroke-group + :stroke-alignment :stroke-group + :rx :radius-group + :ry :radius-group + :selrect :geometry-group + :points :geometry-group + :locked :geometry-group + :proportion :geometry-group + :proportion-lock :geometry-group + :x :geometry-group + :y :geometry-group + :width :geometry-group + :height :geometry-group + :transform :geometry-group + :transform-inverse :geometry-group + :shadow :shadow-group + :blur :blur-group + :masked-group? :mask-group}) + diff --git a/common/app/common/pages_helpers.cljc b/common/app/common/pages/helpers.cljc similarity index 99% rename from common/app/common/pages_helpers.cljc rename to common/app/common/pages/helpers.cljc index 7c5aaa5b0..f1435b33e 100644 --- a/common/app/common/pages_helpers.cljc +++ b/common/app/common/pages/helpers.cljc @@ -7,7 +7,7 @@ ;; ;; Copyright (c) 2020 UXBOX Labs SL -(ns app.common.pages-helpers +(ns app.common.pages.helpers (:require [app.common.data :as d] [app.common.geom.shapes :as gsh] diff --git a/common/app/common/pages/init.cljc b/common/app/common/pages/init.cljc new file mode 100644 index 000000000..d39ecf1fe --- /dev/null +++ b/common/app/common/pages/init.cljc @@ -0,0 +1,143 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.common.pages.init + (:require + [app.common.data :as d] + [app.common.uuid :as uuid] + [app.common.exceptions :as ex] + [app.common.pages.common :refer [file-version default-color]])) + +(def root uuid/zero) + +(def empty-page-data + {:options {} + :name "Page" + :objects + {root + {:id root + :type :frame + :name "Root Frame"}}}) + +(def empty-file-data + {:version file-version + :pages [] + :pages-index {}}) + +(def default-shape-attrs + {:fill-color default-color + :fill-opacity 1}) + +(def default-frame-attrs + {:frame-id uuid/zero + :fill-color "#ffffff" + :fill-opacity 1 + :shapes []}) + +(def ^:private minimal-shapes + [{:type :rect + :name "Rect" + :fill-color default-color + :fill-opacity 1 + :stroke-style :none + :stroke-alignment :center + :stroke-width 0 + :stroke-color "#000000" + :stroke-opacity 0 + :rx 0 + :ry 0} + + {:type :image} + + {:type :icon} + + {:type :circle + :name "Circle" + :fill-color default-color + :fill-opacity 1 + :stroke-style :none + :stroke-alignment :center + :stroke-width 0 + :stroke-color "#000000" + :stroke-opacity 0} + + {:type :path + :name "Path" + :fill-color "#000000" + :fill-opacity 0 + :stroke-style :solid + :stroke-alignment :center + :stroke-width 2 + :stroke-color "#000000" + :stroke-opacity 1} + + {:type :frame + :name "Artboard" + :fill-color "#ffffff" + :fill-opacity 1 + :stroke-style :none + :stroke-alignment :center + :stroke-width 0 + :stroke-color "#000000" + :stroke-opacity 0} + + {:type :text + :name "Text" + :content nil}]) + +(defn make-minimal-shape + [type] + (let [type (cond (= type :curve) :path + :else type) + shape (d/seek #(= type (:type %)) minimal-shapes)] + (when-not shape + (ex/raise :type :assertion + :code :shape-type-not-implemented + :context {:type type})) + + (cond-> shape + :always + (assoc :id (uuid/next)) + + (not= :path (:type shape)) + (assoc :x 0 + :y 0 + :width 1 + :height 1 + :selrect {:x 0 + :y 0 + :x1 0 + :y1 0 + :x2 1 + :y2 1 + :width 1 + :height 1})))) + +(defn make-minimal-group + [frame-id selection-rect group-name] + {:id (uuid/next) + :type :group + :name group-name + :shapes [] + :frame-id frame-id + :x (:x selection-rect) + :y (:y selection-rect) + :width (:width selection-rect) + :height (:height selection-rect)}) + +(defn make-file-data + ([] (make-file-data (uuid/next))) + ([id] + (let [ + pd (assoc empty-page-data + :id id + :name "Page-1")] + (-> empty-file-data + (update :pages conj id) + (update :pages-index assoc id pd))))) diff --git a/common/app/common/pages_migrations.cljc b/common/app/common/pages/migrations.cljc similarity index 89% rename from common/app/common/pages_migrations.cljc rename to common/app/common/pages/migrations.cljc index ef16a5e54..5c65aa9ed 100644 --- a/common/app/common/pages_migrations.cljc +++ b/common/app/common/pages/migrations.cljc @@ -1,4 +1,13 @@ -(ns app.common.pages-migrations +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.common.pages.migrations (:require [app.common.pages :as cp] [app.common.geom.shapes :as gsh] diff --git a/common/app/common/pages/spec.cljc b/common/app/common/pages/spec.cljc new file mode 100644 index 000000000..1cd0a4dfa --- /dev/null +++ b/common/app/common/pages/spec.cljc @@ -0,0 +1,571 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns app.common.pages.spec + (:require + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] + [app.common.spec :as us] + [clojure.spec.alpha :as s])) + +;; --- Specs + +(s/def ::frame-id uuid?) +(s/def ::id uuid?) +(s/def ::name string?) +(s/def ::page-id uuid?) +(s/def ::parent-id uuid?) +(s/def ::string string?) +(s/def ::type keyword?) +(s/def ::uuid uuid?) + +(s/def ::component-id uuid?) +(s/def ::component-file uuid?) +(s/def ::component-root? boolean?) +(s/def ::shape-ref uuid?) + +(s/def ::safe-integer ::us/safe-integer) +(s/def ::safe-number ::us/safe-number) + +(s/def :internal.matrix/a ::us/safe-number) +(s/def :internal.matrix/b ::us/safe-number) +(s/def :internal.matrix/c ::us/safe-number) +(s/def :internal.matrix/d ::us/safe-number) +(s/def :internal.matrix/e ::us/safe-number) +(s/def :internal.matrix/f ::us/safe-number) + +(s/def ::matrix + (s/and (s/keys :req-un [:internal.matrix/a + :internal.matrix/b + :internal.matrix/c + :internal.matrix/d + :internal.matrix/e + :internal.matrix/f]) + gmt/matrix?)) + + +(s/def :internal.point/x ::us/safe-number) +(s/def :internal.point/y ::us/safe-number) + +(s/def ::point + (s/and (s/keys :req-un [:internal.point/x + :internal.point/y]) + gpt/point?)) + +;; GRADIENTS + +(s/def :internal.gradient.stop/color ::string) +(s/def :internal.gradient.stop/opacity ::safe-number) +(s/def :internal.gradient.stop/offset ::safe-number) + +(s/def :internal.gradient/type #{:linear :radial}) +(s/def :internal.gradient/start-x ::safe-number) +(s/def :internal.gradient/start-y ::safe-number) +(s/def :internal.gradient/end-x ::safe-number) +(s/def :internal.gradient/end-y ::safe-number) +(s/def :internal.gradient/width ::safe-number) + +(s/def :internal.gradient/stop + (s/keys :req-un [:internal.gradient.stop/color + :internal.gradient.stop/opacity + :internal.gradient.stop/offset])) + +(s/def :internal.gradient/stops + (s/coll-of :internal.gradient/stop :kind vector?)) + +(s/def ::gradient + (s/keys :req-un [:internal.gradient/type + :internal.gradient/start-x + :internal.gradient/start-y + :internal.gradient/end-x + :internal.gradient/end-y + :internal.gradient/width + :internal.gradient/stops])) + + +;;; COLORS + +(s/def :internal.color/name ::string) +(s/def :internal.color/value (s/nilable ::string)) +(s/def :internal.color/color (s/nilable ::string)) +(s/def :internal.color/opacity (s/nilable ::safe-number)) +(s/def :internal.color/gradient (s/nilable ::gradient)) + +(s/def ::color + (s/keys :opt-un [::id + :internal.color/name + :internal.color/value + :internal.color/color + :internal.color/opacity + :internal.color/gradient])) + + + +;;; SHADOW EFFECT + +(s/def :internal.shadow/id uuid?) +(s/def :internal.shadow/style #{:drop-shadow :inner-shadow}) +(s/def :internal.shadow/color ::color) +(s/def :internal.shadow/offset-x ::safe-number) +(s/def :internal.shadow/offset-y ::safe-number) +(s/def :internal.shadow/blur ::safe-number) +(s/def :internal.shadow/spread ::safe-number) +(s/def :internal.shadow/hidden boolean?) + +(s/def :internal.shadow/shadow + (s/keys :req-un [:internal.shadow/id + :internal.shadow/style + :internal.shadow/color + :internal.shadow/offset-x + :internal.shadow/offset-y + :internal.shadow/blur + :internal.shadow/spread + :internal.shadow/hidden])) + +(s/def ::shadow + (s/coll-of :internal.shadow/shadow :kind vector?)) + + +;;; BLUR EFFECT + +(s/def :internal.blur/id uuid?) +(s/def :internal.blur/type #{:layer-blur}) +(s/def :internal.blur/value ::safe-number) +(s/def :internal.blur/hidden boolean?) + +(s/def ::blur + (s/keys :req-un [:internal.blur/id + :internal.blur/type + :internal.blur/value + :internal.blur/hidden])) + +;; Page Options +(s/def :internal.page.grid.color/value string?) +(s/def :internal.page.grid.color/opacity ::safe-number) + +(s/def :internal.page.grid/size ::safe-integer) +(s/def :internal.page.grid/color + (s/keys :req-un [:internal.page.grid.color/value + :internal.page.grid.color/opacity])) + +(s/def :internal.page.grid/type #{:stretch :left :center :right}) +(s/def :internal.page.grid/item-length (s/nilable ::safe-integer)) +(s/def :internal.page.grid/gutter (s/nilable ::safe-integer)) +(s/def :internal.page.grid/margin (s/nilable ::safe-integer)) + +(s/def :internal.page.grid/square + (s/keys :req-un [:internal.page.grid/size + :internal.page.grid/color])) + +(s/def :internal.page.grid/column + (s/keys :req-un [:internal.page.grid/size + :internal.page.grid/color + :internal.page.grid/type + :internal.page.grid/item-length + :internal.page.grid/gutter + :internal.page.grid/margin])) + +(s/def :internal.page.grid/row :internal.page.grid/column) + +(s/def :internal.page.options/background string?) +(s/def :internal.page.options/saved-grids + (s/keys :req-un [:internal.page.grid/square + :internal.page.grid/row + :internal.page.grid/column])) + +(s/def :internal.page/options + (s/keys :opt-un [:internal.page.options/background])) + +;; Interactions + +(s/def :internal.shape.interaction/event-type #{:click}) ; In the future we will have more options +(s/def :internal.shape.interaction/action-type #{:navigate}) +(s/def :internal.shape.interaction/destination ::uuid) + +(s/def :internal.shape/interaction + (s/keys :req-un [:internal.shape.interaction/event-type + :internal.shape.interaction/action-type + :internal.shape.interaction/destination])) + +(s/def :internal.shape/interactions + (s/coll-of :internal.shape/interaction :kind vector?)) + +;; Page Data related +(s/def :internal.shape/blocked boolean?) +(s/def :internal.shape/collapsed boolean?) +(s/def :internal.shape/content any?) + +(s/def :internal.shape/fill-color string?) +(s/def :internal.shape/fill-opacity ::safe-number) +(s/def :internal.shape/fill-color-gradient (s/nilable ::gradient)) +(s/def :internal.shape/fill-color-ref-file (s/nilable uuid?)) +(s/def :internal.shape/fill-color-ref-id (s/nilable uuid?)) + +(s/def :internal.shape/font-family string?) +(s/def :internal.shape/font-size ::safe-integer) +(s/def :internal.shape/font-style string?) +(s/def :internal.shape/font-weight string?) +(s/def :internal.shape/hidden boolean?) +(s/def :internal.shape/letter-spacing ::safe-number) +(s/def :internal.shape/line-height ::safe-number) +(s/def :internal.shape/locked boolean?) +(s/def :internal.shape/page-id uuid?) +(s/def :internal.shape/proportion ::safe-number) +(s/def :internal.shape/proportion-lock boolean?) +(s/def :internal.shape/rx ::safe-number) +(s/def :internal.shape/ry ::safe-number) +(s/def :internal.shape/stroke-color string?) +(s/def :internal.shape/stroke-color-gradient (s/nilable ::gradient)) +(s/def :internal.shape/stroke-color-ref-file (s/nilable uuid?)) +(s/def :internal.shape/stroke-color-ref-id (s/nilable uuid?)) +(s/def :internal.shape/stroke-opacity ::safe-number) +(s/def :internal.shape/stroke-style #{:solid :dotted :dashed :mixed :none}) +(s/def :internal.shape/stroke-width ::safe-number) +(s/def :internal.shape/stroke-alignment #{:center :inner :outer}) +(s/def :internal.shape/text-align #{"left" "right" "center" "justify"}) +(s/def :internal.shape/x ::safe-number) +(s/def :internal.shape/y ::safe-number) +(s/def :internal.shape/cx ::safe-number) +(s/def :internal.shape/cy ::safe-number) +(s/def :internal.shape/width ::safe-number) +(s/def :internal.shape/height ::safe-number) +(s/def :internal.shape/index integer?) +(s/def :internal.shape/shadow ::shadow) +(s/def :internal.shape/blur ::blur) + +(s/def :internal.shape/x1 ::safe-number) +(s/def :internal.shape/y1 ::safe-number) +(s/def :internal.shape/x2 ::safe-number) +(s/def :internal.shape/y2 ::safe-number) + +(s/def :internal.shape.export/suffix string?) +(s/def :internal.shape.export/scale ::safe-number) +(s/def :internal.shape/export + (s/keys :req-un [::type + :internal.shape.export/suffix + :internal.shape.export/scale])) + +(s/def :internal.shape/exports + (s/coll-of :internal.shape/export :kind vector?)) + +(s/def :internal.shape/selrect + (s/keys :req-un [:internal.shape/x + :internal.shape/y + :internal.shape/x1 + :internal.shape/y1 + :internal.shape/x2 + :internal.shape/y2 + :internal.shape/width + :internal.shape/height])) + +(s/def :internal.shape/points + (s/every ::point :kind vector?)) + +(s/def :internal.shape/shapes + (s/every uuid? :kind vector?)) + +(s/def :internal.shape/transform ::matrix) +(s/def :internal.shape/transform-inverse ::matrix) + +(s/def ::shape-attrs + (s/keys :opt-un [:internal.shape/selrect + :internal.shape/points + :internal.shape/blocked + :internal.shape/collapsed + :internal.shape/content + :internal.shape/fill-color + :internal.shape/fill-opacity + :internal.shape/fill-color-gradient + :internal.shape/fill-color-ref-file + :internal.shape/fill-color-ref-id + :internal.shape/font-family + :internal.shape/font-size + :internal.shape/font-style + :internal.shape/font-weight + :internal.shape/hidden + :internal.shape/letter-spacing + :internal.shape/line-height + :internal.shape/locked + :internal.shape/proportion + :internal.shape/proportion-lock + :internal.shape/rx + :internal.shape/ry + :internal.shape/x + :internal.shape/y + :internal.shape/exports + :internal.shape/shapes + :internal.shape/stroke-color + :internal.shape/stroke-color-ref-file + :internal.shape/stroke-color-ref-id + :internal.shape/stroke-opacity + :internal.shape/stroke-style + :internal.shape/stroke-width + :internal.shape/stroke-alignment + :internal.shape/text-align + :internal.shape/transform + :internal.shape/transform-inverse + :internal.shape/width + :internal.shape/height + :internal.shape/interactions + :internal.shape/masked-group? + :internal.shape/shadow + :internal.shape/blur])) + + + ;; shapes-group is handled differently + +(s/def ::minimal-shape + (s/keys :req-un [::type ::name] + :opt-un [::id])) + +(s/def ::shape + (s/and ::minimal-shape ::shape-attrs + (s/keys :opt-un [::id + ::component-id + ::component-file + ::component-root? + ::shape-ref]))) + +(s/def :internal.page/objects (s/map-of uuid? ::shape)) + +(s/def ::page + (s/keys :req-un [::id + ::name + :internal.page/options + :internal.page/objects])) + + +(s/def ::recent-color + (s/keys :opt-un [:internal.color/value + :internal.color/color + :internal.color/opacity + :internal.color/gradient])) + +(s/def :internal.media-object/name ::string) +(s/def :internal.media-object/path ::string) +(s/def :internal.media-object/width ::safe-integer) +(s/def :internal.media-object/height ::safe-integer) +(s/def :internal.media-object/mtype ::string) +(s/def :internal.media-object/thumb-path ::string) +(s/def :internal.media-object/thumb-width ::safe-integer) +(s/def :internal.media-object/thumb-height ::safe-integer) +(s/def :internal.media-object/thumb-mtype ::string) + +(s/def ::media-object + (s/keys :req-un [::id ::name + :internal.media-object/name + :internal.media-object/path + :internal.media-object/width + :internal.media-object/height + :internal.media-object/mtype + :internal.media-object/thumb-path])) + +(s/def ::media-object-update + (s/keys :req-un [::id] + :req-opt [::name + :internal.media-object/name + :internal.media-object/path + :internal.media-object/width + :internal.media-object/height + :internal.media-object/mtype + :internal.media-object/thumb-path])) + +(s/def :internal.file/colors + (s/map-of ::uuid ::color)) + +(s/def :internal.file/recent-colors + (s/coll-of ::recent-color :kind vector?)) + +(s/def :internal.typography/id ::id) +(s/def :internal.typography/name ::string) +(s/def :internal.typography/font-id ::string) +(s/def :internal.typography/font-family ::string) +(s/def :internal.typography/font-variant-id ::string) +(s/def :internal.typography/font-size ::string) +(s/def :internal.typography/font-weight ::string) +(s/def :internal.typography/font-style ::string) +(s/def :internal.typography/line-height ::string) +(s/def :internal.typography/letter-spacing ::string) +(s/def :internal.typography/text-transform ::string) + +(s/def ::typography + (s/keys :req-un [:internal.typography/id + :internal.typography/name + :internal.typography/font-id + :internal.typography/font-family + :internal.typography/font-variant-id + :internal.typography/font-size + :internal.typography/font-weight + :internal.typography/font-style + :internal.typography/line-height + :internal.typography/letter-spacing + :internal.typography/text-transform])) + +(s/def :internal.file/pages + (s/coll-of ::uuid :kind vector?)) + +(s/def :internal.file/media + (s/map-of ::uuid ::media-object)) + +(s/def :internal.file/pages-index + (s/map-of ::uuid ::page)) + +(s/def ::data + (s/keys :req-un [:internal.file/pages-index + :internal.file/pages] + :opt-un [:internal.file/colors + :internal.file/recent-colors + :internal.file/media])) + +(s/def :internal.container/type #{:page :component}) + +(s/def ::container + (s/keys :req-un [:internal.container/type + ::id + ::name + :internal.page/objects])) + +(defmulti operation-spec :type) + +(s/def :internal.operations.set/attr keyword?) +(s/def :internal.operations.set/val any?) +(s/def :internal.operations.set/touched + (s/nilable (s/every keyword? :kind set?))) + +(defmethod operation-spec :set [_] + (s/keys :req-un [:internal.operations.set/attr + :internal.operations.set/val])) + +(defmethod operation-spec :set-touched [_] + (s/keys :req-un [:internal.operations.set/touched])) + +(defmulti change-spec :type) + +(s/def :internal.changes.set-option/option any?) +(s/def :internal.changes.set-option/value any?) + +(defmethod change-spec :set-option [_] + (s/keys :req-un [:internal.changes.set-option/option + :internal.changes.set-option/value])) + +(s/def :internal.changes.add-obj/obj ::shape) + +(defn- valid-container-id-frame? + [o] + (or (and (contains? o :page-id) + (not (contains? o :component-id)) + (some? (:frame-id o))) + (and (contains? o :component-id) + (not (contains? o :page-id)) + (nil? (:frame-id o))))) + +(defn- valid-container-id? + [o] + (or (and (contains? o :page-id) + (not (contains? o :component-id))) + (and (contains? o :component-id) + (not (contains? o :page-id))))) + +(defmethod change-spec :add-obj [_] + (s/and (s/keys :req-un [::id :internal.changes.add-obj/obj] + :opt-un [::page-id ::component-id ::parent-id ::frame-id]) + valid-container-id-frame?)) + +(s/def ::operation (s/multi-spec operation-spec :type)) +(s/def ::operations (s/coll-of ::operation)) + +(defmethod change-spec :mod-obj [_] + (s/and (s/keys :req-un [::id ::operations] + :opt-un [::page-id ::component-id]) + valid-container-id?)) + +(defmethod change-spec :del-obj [_] + (s/and (s/keys :req-un [::id] + :opt-un [::page-id ::component-id]) + valid-container-id?)) + +(s/def :internal.changes.reg-objects/shapes + (s/coll-of uuid? :kind vector?)) + +(defmethod change-spec :reg-objects [_] + (s/and (s/keys :req-un [:internal.changes.reg-objects/shapes] + :opt-un [::page-id ::component-id]) + valid-container-id?)) + +(defmethod change-spec :mov-objects [_] + (s/and (s/keys :req-un [::parent-id :internal.shape/shapes] + :opt-un [::page-id ::component-id ::index]) + valid-container-id?)) + +(defmethod change-spec :add-page [_] + (s/or :empty (s/keys :req-un [::id ::name]) + :complete (s/keys :req-un [::page]))) + +(defmethod change-spec :mod-page [_] + (s/keys :req-un [::id ::name])) + +(defmethod change-spec :del-page [_] + (s/keys :req-un [::id])) + +(defmethod change-spec :mov-page [_] + (s/keys :req-un [::id ::index])) + +(defmethod change-spec :add-color [_] + (s/keys :req-un [::color])) + +(defmethod change-spec :mod-color [_] + (s/keys :req-un [::color])) + +(defmethod change-spec :del-color [_] + (s/keys :req-un [::id])) + +(s/def :internal.changes.add-recent-color/color ::recent-color) + +(defmethod change-spec :add-recent-color [_] + (s/keys :req-un [:internal.changes.add-recent-color/color])) + +(s/def :internal.changes.media/object ::media-object) + +(defmethod change-spec :add-media [_] + (s/keys :req-un [:internal.changes.media/object])) + +(s/def :internal.changes.media.mod/object ::media-object-update) + +(defmethod change-spec :mod-media [_] + (s/keys :req-un [:internal.changes.media.mod/object])) + +(defmethod change-spec :del-media [_] + (s/keys :req-un [::id])) + +(s/def :internal.changes.add-component/shapes + (s/coll-of ::shape)) + +(defmethod change-spec :add-component [_] + (s/keys :req-un [::id ::name :internal.changes.add-component/shapes])) + +(defmethod change-spec :mod-component [_] + (s/keys :req-un [::id] + :opt-un [::name :internal.changes.add-component/shapes])) + +(defmethod change-spec :del-component [_] + (s/keys :req-un [::id])) + +(s/def :internal.changes.typography/typography ::typography) + +(defmethod change-spec :add-typography [_] + (s/keys :req-un [:internal.changes.typography/typography])) + +(defmethod change-spec :mod-typography [_] + (s/keys :req-un [:internal.changes.typography/typography])) + +(defmethod change-spec :del-typography [_] + (s/keys :req-un [:internal.typography/id])) + +(s/def ::change (s/multi-spec change-spec :type)) +(s/def ::changes (s/coll-of ::change)) diff --git a/common/app/common/spec.cljc b/common/app/common/spec.cljc index 929396e91..10a174deb 100644 --- a/common/app/common/spec.cljc +++ b/common/app/common/spec.cljc @@ -31,6 +31,9 @@ (def uuid-rx #"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") +(def max-safe-int (int 1e6)) +(def min-safe-int (int -1e6)) + ;; --- Conformers (defn- uuid-conformer @@ -118,9 +121,6 @@ (s/def ::point gpt/point?) (s/def ::id ::uuid) -(def max-safe-int 1000000) -(def min-safe-int -1000000) - (s/def ::safe-integer #(and (integer? %) diff --git a/frontend/src/app/main/data/colors.cljs b/frontend/src/app/main/data/colors.cljs index ae866dd50..c84529a2b 100644 --- a/frontend/src/app/main/data/colors.cljs +++ b/frontend/src/app/main/data/colors.cljs @@ -6,24 +6,24 @@ (ns app.main.data.colors (:require - [cljs.spec.alpha :as s] - [beicon.core :as rx] - [clojure.set :as set] - [potok.core :as ptk] - [app.main.streams :as ms] [app.common.data :as d] + [app.common.pages :as cp] [app.common.spec :as us] + [app.common.uuid :as uuid] + [app.main.data.modal :as md] + [app.main.data.workspace.common :as dwc] + [app.main.data.workspace.texts :as dwt] [app.main.repo :as rp] [app.main.store :as st] + [app.main.streams :as ms] [app.util.color :as color] [app.util.i18n :refer [tr]] [app.util.router :as rt] [app.util.time :as dt] - [app.common.uuid :as uuid] - [app.main.data.workspace.common :as dwc] - [app.main.data.workspace.texts :as dwt] - [app.main.data.modal :as md] - [app.common.pages-helpers :as cph])) + [beicon.core :as rx] + [cljs.spec.alpha :as s] + [clojure.set :as set] + [potok.core :as ptk])) (def clear-color-for-rename (ptk/reify ::clear-color-for-rename @@ -112,7 +112,7 @@ (let [pid (:current-page-id state) objects (get-in state [:workspace-data :pages-index pid :objects]) not-frame (fn [shape-id] (not= (get-in objects [shape-id :type]) :frame)) - children (->> ids (filter not-frame) (mapcat #(cph/get-children % objects))) + children (->> ids (filter not-frame) (mapcat #(cp/get-children % objects))) ids (into ids children) is-text? #(= :text (:type (get objects %))) @@ -141,7 +141,7 @@ (let [pid (:current-page-id state) objects (get-in state [:workspace-data :pages-index pid :objects]) not-frame (fn [shape-id] (not= (get-in objects [shape-id :type]) :frame)) - children (->> ids (filter not-frame) (mapcat #(cph/get-children % objects))) + children (->> ids (filter not-frame) (mapcat #(cp/get-children % objects))) ids (into ids children) update-fn (fn [s] diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index 7dd2e8df2..dd8107898 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -17,7 +17,6 @@ [app.common.geom.shapes :as geom] [app.common.math :as mth] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.spec :as us] [app.common.uuid :as uuid] [app.config :as cfg] diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index 8e7e7a40d..9f1c4ecd5 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -12,7 +12,6 @@ [app.common.data :as d] [app.common.exceptions :as ex] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.spec :as us] [app.common.uuid :as uuid] [app.main.constants :as c] @@ -388,7 +387,7 @@ (conj id))] (-> state (assoc-in [:viewer-local :selected] - (cph/expand-region-selection objects selection))))))) + (cp/expand-region-selection objects selection))))))) (defn select-all [] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 126ed925b..f42a029a0 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -18,7 +18,6 @@ [app.common.geom.align :as gal] [app.common.math :as mth] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.spec :as us] [app.common.uuid :as uuid] [app.config :as cfg] @@ -346,7 +345,7 @@ (initialize [state local] (let [page-id (:current-page-id state) objects (dwc/lookup-page-objects state page-id) - shapes (cph/select-toplevel-shapes objects {:include-frames? true}) + shapes (cp/select-toplevel-shapes objects {:include-frames? true}) srect (gsh/selection-rect shapes) local (assoc local :vport size :zoom 1)] (cond @@ -517,7 +516,7 @@ (update [_ state] (let [page-id (:current-page-id state) objects (dwc/lookup-page-objects state page-id) - shapes (cph/select-toplevel-shapes objects {:include-frames? true}) + shapes (cp/select-toplevel-shapes objects {:include-frames? true}) srect (gsh/selection-rect shapes)] (if (or (mth/nan? (:width srect)) @@ -572,7 +571,7 @@ page-id (:current-page-id state) frame-id (-> (dwc/lookup-page-objects state page-id) - (cph/frame-id-by-position {:x frame-x :y frame-y})) + (cp/frame-id-by-position {:x frame-x :y frame-y})) shape (-> (cp/make-minimal-shape type) (merge data) @@ -686,8 +685,8 @@ rchanges (d/concat (reduce (fn [res id] - (let [children (cph/get-children id objects) - parents (cph/get-parents id objects) + (let [children (cp/get-children id objects) + parents (cp/get-parents id objects) del-change #(array-map :type :del-obj :page-id page-id @@ -713,15 +712,15 @@ uchanges (d/concat (reduce (fn [res id] - (let [children (cph/get-children id objects) - parents (cph/get-parents id objects) + (let [children (cp/get-children id objects) + parents (cp/get-parents id objects) parent (get objects (first parents)) add-change (fn [id] (let [item (get objects id)] {:type :add-obj :id (:id item) :page-id page-id - :index (cph/position-on-parent id objects) + :index (cp/position-on-parent id objects) :frame-id (:frame-id item) :parent-id (:parent-id item) :obj item}))] @@ -802,7 +801,7 @@ :frame-id (:frame-id obj) :page-id page-id :shapes [id] - :index (cph/position-on-parent id objects)})) + :index (cp/position-on-parent id objects)})) selected)] ;; TODO: maybe missing the :reg-objects event? (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))) @@ -826,7 +825,7 @@ (if (nil? ids) (vec res) (recur - (conj res (cph/get-parent (first ids) objects)) + (conj res (cp/get-parent (first ids) objects)) (next ids)))) groups-to-unmask @@ -869,7 +868,7 @@ {:type :mov-objects :parent-id (:parent-id obj) :page-id page-id - :index (cph/position-on-parent id objects) + :index (cp/position-on-parent id objects) :shapes [id]}))) [] (reverse ids)) [{:type :reg-objects @@ -1289,7 +1288,7 @@ [frame-id delta] (if (selected-frame? state) [(first page-selected) (get page-objects (first page-selected))] - [(cph/frame-id-by-position page-objects mouse-pos) + [(cp/frame-id-by-position page-objects mouse-pos) (gpt/subtract mouse-pos orig-pos)]) objects (d/mapm (fn [_ v] (assoc v :frame-id frame-id :parent-id frame-id)) objects) @@ -1328,7 +1327,7 @@ height 16 page-id (:current-page-id state) frame-id (-> (dwc/lookup-page-objects state page-id) - (cph/frame-id-by-position @ms/mouse-position)) + (cp/frame-id-by-position @ms/mouse-position)) shape (gsh/setup-selrect {:id id :type :text diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index 663e037c7..037fa483e 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -15,7 +15,6 @@ [potok.core :as ptk] [app.common.data :as d] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.spec :as us] [app.common.uuid :as uuid] [app.main.worker :as uw] @@ -168,7 +167,7 @@ (defn get-frame-at-point [objects point] - (let [frames (cph/select-frames objects)] + (let [frames (cp/select-frames objects)] (d/seek #(gsh/has-point? % point) frames))) @@ -366,7 +365,7 @@ (let [expand-fn (fn [expanded] (merge expanded (->> ids - (map #(cph/get-parents % objects)) + (map #(cp/get-parents % objects)) flatten (filter #(not= % uuid/zero)) (map (fn [id] {id true})) @@ -423,7 +422,7 @@ (us/assert ::coll-of-uuid ids) (us/assert fn? f) (letfn [(impl-get-children [objects id] - (cons id (cph/get-children id objects))) + (cons id (cp/get-children id objects))) (impl-gen-changes [objects page-id ids] (loop [sids (seq ids) @@ -537,7 +536,7 @@ frame-id (if (= :frame (:type attrs)) uuid/zero (or (:frame-id attrs) - (cph/frame-id-by-position objects attrs))) + (cp/frame-id-by-position objects attrs))) shape (merge (if (= :frame (:type shape)) diff --git a/frontend/src/app/main/data/workspace/drawing/box.cljs b/frontend/src/app/main/data/workspace/drawing/box.cljs index 1519cd365..dc6091372 100644 --- a/frontend/src/app/main/data/workspace/drawing/box.cljs +++ b/frontend/src/app/main/data/workspace/drawing/box.cljs @@ -14,7 +14,7 @@ [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.uuid :as uuid] - [app.common.pages-helpers :as cph] + [app.common.pages :as cp] [app.main.data.workspace.common :as dwc] [app.main.snap :as snap] [app.main.streams :as ms] @@ -62,7 +62,7 @@ objects (dwc/lookup-page-objects state page-id) layout (get state :workspace-layout) - frames (cph/select-frames objects) + frames (cp/select-frames objects) fid (or (->> frames (filter #(gsh/has-point? % initial)) first diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index e5e6b78e1..91d1e32e8 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -3,7 +3,6 @@ [app.common.data :as d] [app.common.geom.shapes :as gsh] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.selection :as dws] [beicon.core :as rx] @@ -14,7 +13,7 @@ (->> selected (map #(get objects %)) (filter #(not= :frame (:type %))) - (map #(assoc % ::index (cph/position-on-parent (:id %) objects))) + (map #(assoc % ::index (cp/position-on-parent (:id %) objects))) (sort-by ::index))) (defn- make-group @@ -61,7 +60,7 @@ (defn prepare-remove-group [page-id group objects] (let [shapes (:shapes group) - parent-id (cph/get-parent (:id group) objects) + parent-id (cp/get-parent (:id group) objects) parent (get objects parent-id) index-in-parent (->> (:shapes parent) (map-indexed vector) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 32cf296a8..855154136 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -12,7 +12,6 @@ [app.common.data :as d] [app.common.spec :as us] [app.common.uuid :as uuid] - [app.common.pages-helpers :as cph] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] [app.main.data.messages :as dm] @@ -303,7 +302,7 @@ (ptk/reify ::duplicate-component ptk/WatchEvent (watch [_ state stream] - (let [component (cph/get-component id + (let [component (cp/get-component id nil (get state :workspace-data) nil) @@ -365,7 +364,7 @@ objects (dwc/lookup-page-objects state page-id) unames (atom (dwc/retrieve-used-names objects)) - frame-id (cph/frame-id-by-position objects (gpt/add orig-pos delta)) + frame-id (cp/frame-id-by-position objects (gpt/add orig-pos delta)) update-new-shape (fn [new-shape original-shape] @@ -405,7 +404,7 @@ (dissoc :component-root?)))) [new-shape new-shapes _] - (cph/clone-object component-shape + (cp/clone-object component-shape nil (get component :objects) update-new-shape) @@ -440,7 +439,7 @@ (watch [_ state stream] (let [page-id (:current-page-id state) objects (dwc/lookup-page-objects state page-id) - shapes (cph/get-object-with-children id objects) + shapes (cp/get-object-with-children id objects) rchanges (map (fn [obj] {:type :mod-obj @@ -517,7 +516,7 @@ (log/info :msg "RESET-COMPONENT of shape" :id (str id)) (let [local-file (get state :workspace-data) libraries (get state :workspace-libraries) - container (cph/get-container (get state :current-page-id) + container (cp/get-container (get state :current-page-id) :page local-file) [rchanges uchanges] diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 8347e9bc1..1cd2461e4 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -12,7 +12,7 @@ [cljs.spec.alpha :as s] [app.common.spec :as us] [app.common.data :as d] - [app.common.pages-helpers :as cph] + [app.common.pages :as cph] ;; TODO: remove this namespace [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] [app.common.pages :as cp] diff --git a/frontend/src/app/main/data/workspace/persistence.cljs b/frontend/src/app/main/data/workspace/persistence.cljs index fe4efc91d..a4db5cce0 100644 --- a/frontend/src/app/main/data/workspace/persistence.cljs +++ b/frontend/src/app/main/data/workspace/persistence.cljs @@ -187,7 +187,6 @@ (s/def ::version ::us/integer) (s/def ::revn ::us/integer) (s/def ::ordering ::us/integer) -(s/def ::metadata (s/nilable ::cp/metadata)) (s/def ::data ::cp/data) (s/def ::file ::dd/file) diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index 28a5aa5ff..629b4bb83 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -18,7 +18,6 @@ [app.common.geom.shapes :as geom] [app.common.math :as mth] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.spec :as us] [app.common.uuid :as uuid] [app.main.data.workspace.common :as dwc] @@ -129,7 +128,7 @@ :pages-index page-id :objects shape-id :blocked] false)))] - (rx/of (->> (cph/select-toplevel-shapes objects) + (rx/of (->> (cp/select-toplevel-shapes objects) (map :id) (filter is-not-blocked) (into lks/empty-linked-set) @@ -235,7 +234,7 @@ name (dwc/generate-unique-name names (:name obj)) renamed-obj (assoc obj :id id :name name) moved-obj (geom/move renamed-obj delta) - frames (cph/select-frames objects) + frames (cp/select-frames objects) parent-id (or parent-id frame-id) children-changes diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 514b59b50..f0c83a67b 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -15,7 +15,6 @@ [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.spec :as us] [app.main.data.workspace.common :as dwc] [app.main.data.workspace.selection :as dws] @@ -242,7 +241,7 @@ (let [position @ms/mouse-position page-id (:current-page-id state) objects (dwc/lookup-page-objects state page-id) - frame-id (cph/frame-id-by-position objects position) + frame-id (cp/frame-id-by-position objects position) moving-shapes (->> ids (map #(get objects %)) @@ -399,20 +398,12 @@ (update-in objects [shape-id :modifiers] #(merge % modifiers))) ;; ID's + Children but remove frame children if the flag is set to false - ids-with-children (concat ids (mapcat #(cph/get-children % objects) + ids-with-children (concat ids (mapcat #(cp/get-children % objects) (filter not-frame-id? ids)))] (d/update-in-when state [:workspace-data :pages-index page-id :objects] #(reduce update-shape % ids-with-children))))))) -(defn rotation-modifiers [center shape angle] - (let [displacement (let [shape-center (gsh/center-shape shape)] - (-> (gmt/matrix) - (gmt/rotate angle center) - (gmt/rotate (- angle) shape-center)))] - {:rotation angle - :displacement displacement})) - ;; Set-rotation is custom because applies different modifiers to each ;; shape adjusting their position. @@ -423,14 +414,14 @@ ([delta-rotation shapes center] (letfn [(rotate-shape [objects angle shape center] - (update-in objects [(:id shape) :modifiers] merge (rotation-modifiers center shape angle))) + (update-in objects [(:id shape) :modifiers] merge (gsh/rotation-modifiers center shape angle))) (rotate-around-center [objects angle center shapes] (reduce #(rotate-shape %1 angle %2 center) objects shapes)) (set-rotation [objects] (let [id->obj #(get objects %) - get-children (fn [shape] (map id->obj (cph/get-children (:id shape) 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)))] @@ -452,7 +443,7 @@ objects1 (get-in state [:workspace-data :pages-index page-id :objects]) ;; ID's + Children ID's - ids-with-children (d/concat [] (mapcat #(cph/get-children % objects1) ids) ids) + ids-with-children (d/concat [] (mapcat #(cp/get-children % objects1) ids) ids) ;; For each shape applies the modifiers by transforming the objects update-shape #(update %1 %2 gsh/transform-shape) diff --git a/frontend/src/app/main/exports.cljs b/frontend/src/app/main/exports.cljs index 6a85274e6..8166ab334 100644 --- a/frontend/src/app/main/exports.cljs +++ b/frontend/src/app/main/exports.cljs @@ -14,7 +14,6 @@ [cuerdas.core :as str] [app.common.uuid :as uuid] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.math :as mth] [app.common.geom.shapes :as gsh] [app.common.geom.align :as gal] @@ -43,7 +42,7 @@ (defn- calculate-dimensions [{:keys [objects] :as data} vport] - (let [shapes (cph/select-toplevel-shapes objects {:include-frames? true}) + (let [shapes (cp/select-toplevel-shapes objects {:include-frames? true}) to-finite (fn [val fallback] (if (not (mth/finite? val)) fallback val)) rect (->> (gsh/selection-rect shapes) (gal/adjust-to-viewport vport))] @@ -144,7 +143,7 @@ frame-id (:id frame) - modifier-ids (concat [frame-id] (cph/get-children frame-id objects)) + modifier-ids (concat [frame-id] (cp/get-children frame-id objects)) update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) objects (reduce update-fn objects modifier-ids) frame (assoc-in frame [:modifiers :displacement] modifier) @@ -174,7 +173,7 @@ group-id (:id group) - modifier-ids (concat [group-id] (cph/get-children group-id objects)) + modifier-ids (concat [group-id] (cp/get-children group-id objects)) update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) objects (reduce update-fn objects modifier-ids) group (assoc-in group [:modifiers :displacement] modifier) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index ec587dbdd..e2b13677f 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -13,7 +13,6 @@ [beicon.core :as rx] [okulary.core :as l] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.uuid :as uuid] [app.main.constants :as c] [app.main.store :as st])) @@ -142,7 +141,7 @@ (l/derived :options workspace-page)) (def workspace-frames - (l/derived cph/select-frames workspace-page-objects)) + (l/derived cp/select-frames workspace-page-objects)) (defn object-by-id [id] @@ -162,7 +161,7 @@ (let [page-id (:current-page-id state) objects (get-in state [:workspace-data :pages-index page-id :objects]) selected (get-in state [:workspace-local :selected]) - children (cph/get-children id objects)] + children (cp/get-children id objects)] (some selected children)))] (l/derived selector st/state))) @@ -181,7 +180,7 @@ (let [selected (get-in state [:workspace-local :selected]) page-id (:current-page-id state) objects (get-in state [:workspace-data :pages-index page-id :objects]) - children (mapcat #(cph/get-children % objects) selected)] + children (mapcat #(cp/get-children % objects) selected)] (into selected children)))] (l/derived selector st/state =))) @@ -192,7 +191,7 @@ (let [selected (get-in state [:workspace-local :selected]) page-id (:current-page-id state) objects (get-in state [:workspace-data :pages-index page-id :objects]) - children (mapcat #(cph/get-children % objects) selected) + children (mapcat #(cp/get-children % objects) selected) shapes (into selected children)] (mapv #(get objects %) shapes)))] (l/derived selector st/state =))) diff --git a/frontend/src/app/main/store.cljs b/frontend/src/app/main/store.cljs index 4b06ecce0..333756e36 100644 --- a/frontend/src/app/main/store.cljs +++ b/frontend/src/app/main/store.cljs @@ -12,7 +12,7 @@ [potok.core :as ptk] [cuerdas.core :as str] [app.common.data :as d] - [app.common.pages-helpers :as cph] + [app.common.pages :as cp] [app.common.uuid :as uuid] [app.util.storage :refer [storage]] [app.util.debug :refer [debug? debug-exclude-events logjs]])) @@ -119,7 +119,7 @@ (show-component [shape objects] (if (nil? (:shape-ref shape)) "" - (let [root-shape (cph/get-root-shape shape objects) + (let [root-shape (cp/get-root-shape shape objects) component-id (when root-shape (:component-id root-shape)) component-file-id (when root-shape (:component-file root-shape)) component-file (when component-file-id (get libraries component-file-id)) diff --git a/frontend/src/app/main/ui/handoff/render.cljs b/frontend/src/app/main/ui/handoff/render.cljs index 714abbc6e..7af944e29 100644 --- a/frontend/src/app/main/ui/handoff/render.cljs +++ b/frontend/src/app/main/ui/handoff/render.cljs @@ -15,7 +15,6 @@ [app.util.dom :as dom] [app.common.data :as d] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] @@ -141,7 +140,7 @@ (gmt/translate-matrix)) update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) - modifier-ids (d/concat [frame-id] (cph/get-children frame-id objects))] + modifier-ids (d/concat [frame-id] (cp/get-children frame-id objects))] (reduce update-fn objects modifier-ids))) (defn make-vbox [frame] diff --git a/frontend/src/app/main/ui/render.cljs b/frontend/src/app/main/ui/render.cljs index ec4b00d1f..35612bdab 100644 --- a/frontend/src/app/main/ui/render.cljs +++ b/frontend/src/app/main/ui/render.cljs @@ -14,7 +14,6 @@ [rumext.alpha :as mf] [app.common.uuid :as uuid] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.math :as mth] [app.common.geom.shapes :as geom] [app.common.geom.point :as gpt] @@ -34,7 +33,7 @@ (gpt/negate) (gmt/translate-matrix)) - mod-ids (cons frame-id (cph/get-children frame-id objects)) + mod-ids (cons frame-id (cp/get-children frame-id objects)) updt-fn #(-> %1 (assoc-in [%2 :modifiers :displacement] modifier) (update %2 geom/transform-shape)) @@ -82,7 +81,7 @@ [objects object-id] (if (uuid/zero? object-id) (let [object (get objects object-id) - shapes (cph/select-toplevel-shapes objects {:include-frames? true}) + shapes (cp/select-toplevel-shapes objects {:include-frames? true}) srect (geom/selection-rect shapes) object (merge object (select-keys srect [:x :y :width :height])) object (geom/transform-shape object) diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 74fc8f162..94cefd316 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -14,7 +14,7 @@ [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as geom] - [app.common.pages-helpers :as cph] + [app.common.pages :as cp] [app.main.data.viewer :as dv] [app.main.data.comments :as dcm] [app.main.refs :as refs] @@ -143,7 +143,7 @@ update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) - objects (->> (d/concat [frame-id] (cph/get-children frame-id objects)) + objects (->> (d/concat [frame-id] (cp/get-children frame-id objects)) (reduce update-fn objects)) interactions? (:interactions-show? state) diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 6327a66d9..d27dfe525 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -13,7 +13,6 @@ [rumext.alpha :as mf] [app.common.data :as d] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.main.data.viewer :as dv] [app.main.refs :as refs] [app.main.store :as st] @@ -174,7 +173,7 @@ update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) frame-id (:id frame) - modifier-ids (d/concat [frame-id] (cph/get-children frame-id objects)) + modifier-ids (d/concat [frame-id] (cp/get-children frame-id objects)) objects (reduce update-fn objects modifier-ids) frame (assoc-in frame [:modifiers :displacement] modifier) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 875883148..3b28d8daa 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -14,7 +14,6 @@ [app.common.geom.shapes :as geom] [app.common.media :as cm] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.uuid :as uuid] [app.config :as cfg] [app.main.data.colors :as dc] diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index bd9e012af..9ac44acf4 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -11,7 +11,6 @@ (:require [app.common.data :as d] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.uuid :as uuid] [app.main.data.workspace :as dw] [app.main.data.workspace.common :as dwc] @@ -168,7 +167,7 @@ (if (= side :center) (st/emit! (dw/relocate-selected-shapes (:id item) 0)) (let [to-index (if (= side :top) (inc index) index) - parent-id (cph/get-parent (:id item) objects)] + parent-id (cp/get-parent (:id item) objects)] (st/emit! (dw/relocate-selected-shapes parent-id to-index))))) on-hold @@ -245,7 +244,7 @@ old-obs (unchecked-get oprops "objects")] (and (= new-itm old-itm) (identical? new-idx old-idx) - (let [childs (cph/get-children (:id new-itm) new-obs) + (let [childs (cp/get-children (:id new-itm) new-obs) childs' (conj childs (:id new-itm))] (and (or (= new-sel old-sel) (not (or (boolean (some new-sel childs')) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/component.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/component.cljs index 41fe6bd19..0d4a125b7 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/component.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/component.cljs @@ -10,7 +10,7 @@ (ns app.main.ui.workspace.sidebar.options.component (:require [rumext.alpha :as mf] - [app.common.pages-helpers :as cph] + [app.common.pages :as cp] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.icons :as i] @@ -32,10 +32,10 @@ show? (some? (:component-id values)) local-library (mf/deref refs/workspace-local-library) libraries (mf/deref refs/workspace-libraries) - component (cph/get-component (:component-id values) - (:component-file values) - local-library - libraries) + component (cp/get-component (:component-id values) + (:component-file values) + local-library + libraries) on-menu-click (mf/use-callback (fn [event] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/group.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/group.cljs index 4dac6c4d8..4554a9530 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/group.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/group.cljs @@ -13,7 +13,6 @@ [rumext.alpha :as mf] [app.common.attrs :as attrs] [app.common.geom.shapes :as geom] - [app.common.pages-helpers :as cph] [app.main.refs :as refs] [app.main.data.workspace.texts :as dwt] [app.main.ui.workspace.sidebar.options.multiple :refer [get-shape-attrs]] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/interactions.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/interactions.cljs index bfa6c576e..98f14837c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/interactions.cljs @@ -11,7 +11,7 @@ (:require [rumext.alpha :as mf] [app.common.data :as d] - [app.common.pages-helpers :as cph] + [app.common.pages :as cp] [app.main.data.workspace :as dw] [app.main.refs :as refs] [app.main.store :as st] @@ -32,7 +32,7 @@ destination (get objects (:destination interaction)) frames (mf/use-memo (mf/deps objects) - #(cph/select-frames objects)) + #(cp/select-frames objects)) show-frames-dropdown? (mf/use-state false) diff --git a/frontend/src/app/worker/selection.cljs b/frontend/src/app/worker/selection.cljs index c41de8a86..0372abf46 100644 --- a/frontend/src/app/worker/selection.cljs +++ b/frontend/src/app/worker/selection.cljs @@ -15,7 +15,6 @@ [app.common.exceptions :as ex] [app.common.geom.shapes :as geom] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.spec :as us] [app.common.uuid :as uuid] [app.util.quadtree :as qdt] @@ -65,7 +64,7 @@ (defn- create-index [objects] - (let [shapes (cph/select-toplevel-shapes objects {:include-frames? true}) + (let [shapes (cp/select-toplevel-shapes objects {:include-frames? true}) bounds (geom/selection-rect shapes) bounds #js {:x (:x bounds) :y (:y bounds) diff --git a/frontend/src/app/worker/snaps.cljs b/frontend/src/app/worker/snaps.cljs index 4f95a9d40..ecd5ba34d 100644 --- a/frontend/src/app/worker/snaps.cljs +++ b/frontend/src/app/worker/snaps.cljs @@ -12,7 +12,6 @@ [okulary.core :as l] [app.common.uuid :as uuid] [app.common.pages :as cp] - [app.common.pages-helpers :as cph] [app.common.data :as d] [app.worker.impl :as impl] [app.util.range-tree :as rt] @@ -46,7 +45,7 @@ (let [frame-shapes (->> (vals objects) (filter :frame-id) (group-by :frame-id)) - frame-shapes (->> (cph/select-frames objects) + frame-shapes (->> (cp/select-frames objects) (reduce #(update %1 (:id %2) conj %2) frame-shapes))] (d/mapm (fn [frame-id shapes] {:x (create-coord-data frame-id shapes :x)