mirror of
https://github.com/penpot/penpot.git
synced 2025-06-04 01:11:41 +02:00
commit
f5e16eb469
11 changed files with 681 additions and 699 deletions
|
@ -54,6 +54,7 @@
|
|||
(t/is (= [id-b id-c id-a] (get-in res [:objects uuid/zero :shapes])))))))
|
||||
|
||||
(t/deftest process-change-mod-obj
|
||||
(t/testing "simple mod-obj"
|
||||
(let [data cp/default-page-data
|
||||
chg {:type :mod-obj
|
||||
:id uuid/zero
|
||||
|
@ -63,6 +64,16 @@
|
|||
res (cp/process-changes data [chg])]
|
||||
(t/is (= "foobar" (get-in res [:objects uuid/zero :name])))))
|
||||
|
||||
(t/testing "mod-obj for not existing shape"
|
||||
(let [data cp/default-page-data
|
||||
chg {:type :mod-obj
|
||||
:id (uuid/next)
|
||||
:operations [{:type :set
|
||||
:attr :name
|
||||
:val "foobar"}]}
|
||||
res (cp/process-changes data [chg])]
|
||||
(t/is (= res cp/default-page-data)))))
|
||||
|
||||
|
||||
(t/deftest process-change-del-obj-1
|
||||
(let [id (uuid/next)
|
||||
|
@ -202,15 +213,15 @@
|
|||
))
|
||||
|
||||
(t/deftest process-changes-move-objects
|
||||
(let [frame-a-id (uuid/next)
|
||||
frame-b-id (uuid/next)
|
||||
group-a-id (uuid/next)
|
||||
group-b-id (uuid/next)
|
||||
rect-a-id (uuid/next)
|
||||
rect-b-id (uuid/next)
|
||||
rect-c-id (uuid/next)
|
||||
rect-d-id (uuid/next)
|
||||
rect-e-id (uuid/next)
|
||||
(let [frame-a-id (uuid/custom 1)
|
||||
frame-b-id (uuid/custom 2)
|
||||
group-a-id (uuid/custom 3)
|
||||
group-b-id (uuid/custom 4)
|
||||
rect-a-id (uuid/custom 5)
|
||||
rect-b-id (uuid/custom 6)
|
||||
rect-c-id (uuid/custom 7)
|
||||
rect-d-id (uuid/custom 8)
|
||||
rect-e-id (uuid/custom 9)
|
||||
data (-> cp/default-page-data
|
||||
(assoc-in [cp/root :shapes] [frame-a-id])
|
||||
(assoc-in [:objects frame-a-id]
|
||||
|
@ -345,12 +356,47 @@
|
|||
(t/is (= data res))))))
|
||||
|
||||
|
||||
(t/deftest process-changes-move-objects-3
|
||||
(let [shape-2-id (uuid/custom 1 2)
|
||||
shape-3-id (uuid/custom 1 3)
|
||||
frame-id (uuid/custom 1 1)
|
||||
changes [{:type :add-obj
|
||||
:id frame-id
|
||||
:frame-id uuid/zero
|
||||
:obj {:type :frame
|
||||
:name "Frame"}}
|
||||
{:type :add-obj
|
||||
:frame-id frame-id
|
||||
:id shape-2-id
|
||||
:obj {:type :shape
|
||||
:name "Shape"}}
|
||||
{:type :add-obj
|
||||
:id shape-3-id
|
||||
:frame-id uuid/zero
|
||||
:obj {:type :rect
|
||||
:name "Shape"}}]
|
||||
data (cp/process-changes cp/default-page-data changes)]
|
||||
(t/testing "move inside->outside-inside"
|
||||
(let [changes [{:type :mov-objects
|
||||
:shapes [shape-3-id]
|
||||
:parent-id frame-id}
|
||||
{:type :mov-objects
|
||||
:shapes [shape-3-id]
|
||||
:parent-id uuid/zero}]
|
||||
res (cp/process-changes data changes)]
|
||||
|
||||
(t/is (= (get-in res [:objects shape-2-id :frame-id])
|
||||
(get-in data [:objects shape-2-id :frame-id])))
|
||||
(t/is (= (get-in res [:objects shape-3-id :frame-id])
|
||||
(get-in data [:objects shape-3-id :frame-id])))))))
|
||||
|
||||
|
||||
(t/deftest process-changes-move-objects-2
|
||||
(let [shape-1-id (uuid/custom 1 1)
|
||||
shape-2-id (uuid/custom 1 2)
|
||||
shape-3-id (uuid/custom 1 3)
|
||||
shape-4-id (uuid/custom 1 4)
|
||||
group-1-id (uuid/custom 2 1)
|
||||
group-1-id (uuid/custom 1 5)
|
||||
changes [{:type :add-obj
|
||||
:id shape-1-id
|
||||
:frame-id cp/root
|
||||
|
|
|
@ -139,7 +139,7 @@
|
|||
|
||||
(defmethod change-spec-impl :add-obj [_]
|
||||
(s/keys :req-un [::id ::frame-id ::obj]
|
||||
:opt-un [::session-id]))
|
||||
:opt-un [::session-id ::parent-id]))
|
||||
|
||||
(defmethod change-spec-impl :mod-obj [_]
|
||||
(s/keys :req-un [::id ::operations]
|
||||
|
@ -149,10 +149,6 @@
|
|||
(s/keys :req-un [::id]
|
||||
:opt-un [::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :mov-obj [_]
|
||||
(s/keys :req-un [::id ::frame-id]
|
||||
:opt-un [::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :mov-objects [_]
|
||||
(s/keys :req-un [::parent-id ::shapes]
|
||||
:opt-un [::index]))
|
||||
|
@ -194,40 +190,39 @@
|
|||
(defn process-changes
|
||||
[data items]
|
||||
(->> (us/verify ::changes items)
|
||||
(reduce #(or (process-change %1 %2) %1) data)))
|
||||
(reduce #(do
|
||||
;; (prn "process-change" (:type %2) (:id %2))
|
||||
(or (process-change %1 %2) %1))
|
||||
data)))
|
||||
|
||||
(declare insert-at-index)
|
||||
|
||||
(defmethod process-change :add-obj
|
||||
[data {:keys [id obj frame-id index] :as change}]
|
||||
(assert (contains? (:objects data) frame-id) "process-change/add-obj")
|
||||
[data {:keys [id obj frame-id parent-id index] :as change}]
|
||||
(let [parent-id (or parent-id frame-id)
|
||||
objects (:objects data)]
|
||||
(when (and (contains? objects parent-id)
|
||||
(contains? objects frame-id))
|
||||
(let [obj (assoc obj
|
||||
:frame-id frame-id
|
||||
:id id)]
|
||||
(-> data
|
||||
(update :objects assoc id obj)
|
||||
(update-in [:objects frame-id :shapes]
|
||||
(update-in [:objects parent-id :shapes]
|
||||
(fn [shapes]
|
||||
(let [shapes (or shapes [])]
|
||||
(cond
|
||||
(some #{id} shapes) shapes
|
||||
(nil? index) (conj shapes id)
|
||||
:else (insert-at-index shapes index [id])))))))
|
||||
:else (insert-at-index shapes index [id]))))))))))
|
||||
|
||||
(defmethod process-change :mod-obj
|
||||
[data {:keys [id operations] :as change}]
|
||||
(assert (contains? (:objects data) id) "process-change/mod-obj")
|
||||
(update-in data [:objects id]
|
||||
#(reduce process-operation % operations)))
|
||||
|
||||
(defmethod process-change :mov-obj
|
||||
[data {:keys [id frame-id] :as change}]
|
||||
(assert (contains? (:objects data) frame-id))
|
||||
(let [frame-id' (get-in data [:objects id :frame-id])]
|
||||
(when (not= frame-id frame-id')
|
||||
(-> data
|
||||
(update-in [:objects frame-id' :shapes] (fn [s] (filterv #(not= % id) s)))
|
||||
(update-in [:objects id] assoc :frame-id frame-id)
|
||||
(update-in [:objects frame-id :shapes] conj id)))))
|
||||
(update data :objects
|
||||
(fn [objects]
|
||||
(if-let [obj (get objects id)]
|
||||
(assoc objects id (reduce process-operation obj operations))
|
||||
objects))))
|
||||
|
||||
(defmethod process-change :del-obj
|
||||
[data {:keys [id] :as change}]
|
||||
|
@ -283,7 +278,12 @@
|
|||
(let [prev-shapes (or prev-shapes [])]
|
||||
(if index
|
||||
(insert-at-index prev-shapes index shapes)
|
||||
(into prev-shapes shapes))))
|
||||
(reduce (fn [acc id]
|
||||
(if (some #{id} acc)
|
||||
acc
|
||||
(conj acc id)))
|
||||
prev-shapes
|
||||
shapes))))
|
||||
|
||||
strip-id
|
||||
(fn [id]
|
||||
|
@ -316,17 +316,18 @@
|
|||
|
||||
;; Updates the frame-id references that might be outdated
|
||||
update-frame-ids
|
||||
(fn update-frame-ids [data shape-id]
|
||||
(as-> data $
|
||||
(assoc-in $ [:objects shape-id :frame-id] frame-id)
|
||||
(reduce update-frame-ids $ (get-in $ [:objects shape-id :shapes]))))]
|
||||
(fn update-frame-ids [data id]
|
||||
(let [data (assoc-in data [:objects id :frame-id] frame-id)
|
||||
obj (get-in data [:objects id])]
|
||||
(cond-> data
|
||||
(not= :frame (:type obj))
|
||||
(as-> $$ (reduce update-frame-ids $$ (:shapes obj))))))]
|
||||
|
||||
(if valid?
|
||||
(when valid?
|
||||
(as-> data $
|
||||
(update-in $ [:objects parent-id :shapes] insert-items)
|
||||
(reduce remove-in-parent $ shapes)
|
||||
(reduce update-frame-ids $ (get-in $ [:objects parent-id :shapes])))
|
||||
data)))
|
||||
(reduce update-frame-ids $ (get-in $ [:objects parent-id :shapes]))))))
|
||||
|
||||
(defmethod process-operation :set
|
||||
[shape op]
|
||||
|
|
257
frontend/package-lock.json
generated
257
frontend/package-lock.json
generated
|
@ -54,9 +54,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.9.27",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.27.tgz",
|
||||
"integrity": "sha512-j+RvQb9w7a2kZFBOgTh+s/elCwtqWUMN6RJNdmz0ntmwpeoMHKnyhUcmYBu7Yw94Rtj9938D+TJSn6WGcq2+OA==",
|
||||
"version": "16.9.34",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.34.tgz",
|
||||
"integrity": "sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==",
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^2.2.0"
|
||||
|
@ -375,13 +375,13 @@
|
|||
"dev": true
|
||||
},
|
||||
"autoprefixer": {
|
||||
"version": "9.7.5",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.5.tgz",
|
||||
"integrity": "sha512-URo6Zvt7VYifomeAfJlMFnYDhow1rk2bufwkbamPEAtQFcL11moLk4PnR7n9vlu7M+BkXAZkHFA0mIcY7tjQFg==",
|
||||
"version": "9.7.6",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-9.7.6.tgz",
|
||||
"integrity": "sha512-F7cYpbN7uVVhACZTeeIeealwdGM6wMtfWARVLTy5xmKtgVdBNJvbDRoCK3YO1orcs7gv/KwYlb3iXwu9Ug9BkQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"browserslist": "^4.11.0",
|
||||
"caniuse-lite": "^1.0.30001036",
|
||||
"browserslist": "^4.11.1",
|
||||
"caniuse-lite": "^1.0.30001039",
|
||||
"chalk": "^2.4.2",
|
||||
"normalize-range": "^0.1.2",
|
||||
"num2fraction": "^1.2.2",
|
||||
|
@ -650,15 +650,15 @@
|
|||
}
|
||||
},
|
||||
"browserslist": {
|
||||
"version": "4.11.0",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.0.tgz",
|
||||
"integrity": "sha512-WqEC7Yr5wUH5sg6ruR++v2SGOQYpyUdYYd4tZoAq1F7y+QXoLoYGXVbxhtaIqWmAJjtNTRjVD3HuJc1OXTel2A==",
|
||||
"version": "4.11.1",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.11.1.tgz",
|
||||
"integrity": "sha512-DCTr3kDrKEYNw6Jb9HFxVLQNaue8z+0ZfRBRjmCunKDEXEBajKDj2Y+Uelg+Pi29OnvaSGwjOsnRyNEkXzHg5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"caniuse-lite": "^1.0.30001035",
|
||||
"electron-to-chromium": "^1.3.380",
|
||||
"node-releases": "^1.1.52",
|
||||
"pkg-up": "^3.1.0"
|
||||
"caniuse-lite": "^1.0.30001038",
|
||||
"electron-to-chromium": "^1.3.390",
|
||||
"node-releases": "^1.1.53",
|
||||
"pkg-up": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"buffer": {
|
||||
|
@ -731,9 +731,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001038",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001038.tgz",
|
||||
"integrity": "sha512-zii9quPo96XfOiRD4TrfYGs+QsGZpb2cGiMAzPjtf/hpFgB6zCPZgJb7I1+EATeMw/o+lG8FyRAnI+CWStHcaQ==",
|
||||
"version": "1.0.30001042",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001042.tgz",
|
||||
"integrity": "sha512-igMQ4dlqnf4tWv0xjaaE02op9AJ2oQzXKjWf4EuAHFN694Uo9/EfPVIPJcmn2WkU9RqozCxx5e2KPcVClHDbDw==",
|
||||
"dev": true
|
||||
},
|
||||
"caseless": {
|
||||
|
@ -1041,9 +1041,9 @@
|
|||
}
|
||||
},
|
||||
"core-js-pure": {
|
||||
"version": "3.6.4",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz",
|
||||
"integrity": "sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw=="
|
||||
"version": "3.6.5",
|
||||
"resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
|
||||
"integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
|
||||
},
|
||||
"core-util-is": {
|
||||
"version": "1.0.2",
|
||||
|
@ -1139,9 +1139,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"css-selector-parser": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.3.0.tgz",
|
||||
"integrity": "sha1-XxrUPi2O77/cME/NOaUhZklD4+s=",
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/css-selector-parser/-/css-selector-parser-1.4.1.tgz",
|
||||
"integrity": "sha512-HYPSb7y/Z7BNDCOrakL4raGO2zltZkbeXyAd6Tg9obzix6QhzxCotdBl6VT0Dv4vZfJGVz3WL/xaEI9Ly3ul0g==",
|
||||
"dev": true
|
||||
},
|
||||
"css-tree": {
|
||||
|
@ -1200,9 +1200,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"csstype": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.9.tgz",
|
||||
"integrity": "sha512-xz39Sb4+OaTsULgUERcCk+TJj8ylkL4aSVDQiX/ksxbELSqwkgt4d4RD7fovIdgJGSuNYqwZEiVjYY5l0ask+Q=="
|
||||
"version": "2.6.10",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
|
||||
"integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w=="
|
||||
},
|
||||
"d": {
|
||||
"version": "1.0.1",
|
||||
|
@ -1224,9 +1224,9 @@
|
|||
}
|
||||
},
|
||||
"date-fns": {
|
||||
"version": "2.11.1",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.11.1.tgz",
|
||||
"integrity": "sha512-3RdUoinZ43URd2MJcquzBbDQo+J87cSzB8NkXdZiN5ia1UNyep0oCyitfiL88+R7clGTeq/RniXAc16gWyAu1w=="
|
||||
"version": "2.12.0",
|
||||
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.12.0.tgz",
|
||||
"integrity": "sha512-qJgn99xxKnFgB1qL4jpxU7Q2t0LOn1p8KMIveef3UZD7kqjT3tpFNNdXJelEHhE+rUgffriXriw/sOSU+cS1Hw=="
|
||||
},
|
||||
"dateformat": {
|
||||
"version": "3.0.3",
|
||||
|
@ -1461,9 +1461,9 @@
|
|||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.3.390",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.390.tgz",
|
||||
"integrity": "sha512-4RvbM5x+002gKI8sltkqWEk5pptn0UnzekUx8RTThAMPDSb8jjpm6SwGiSnEve7f85biyZl8DMXaipaCxDjXag==",
|
||||
"version": "1.3.409",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.409.tgz",
|
||||
"integrity": "sha512-CB2HUXiMsaVYY5VvcpELhDShiTRhI2FfN7CuacEZ5mDmMFuSG/ZVm8HoSya0+S61RvUd3TjIjFSKywqHZpRPzQ==",
|
||||
"dev": true
|
||||
},
|
||||
"elliptic": {
|
||||
|
@ -1809,9 +1809,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
|
||||
"integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
|
@ -1901,12 +1901,12 @@
|
|||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
|
||||
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
"locate-path": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"findup-sync": {
|
||||
|
@ -2821,13 +2821,13 @@
|
|||
}
|
||||
},
|
||||
"gulp-mustache": {
|
||||
"version": "4.1.2",
|
||||
"resolved": "https://registry.npmjs.org/gulp-mustache/-/gulp-mustache-4.1.2.tgz",
|
||||
"integrity": "sha512-4nJKL6akiP77Znbog2my7XbJ94VnS8HmMBwLYvUNoSYmD99I9vCopIok6XXT1nOu0zXztc5HxQ2PndYt06PWAQ==",
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/gulp-mustache/-/gulp-mustache-5.0.0.tgz",
|
||||
"integrity": "sha512-8tk0R1Fd+l6+e/t954e3UheFo25dKkTapPLD1sWoSroPXfIPxyHVgbhfH5VJGqeXl3te5GOwPtfcxxZJ+PYoFg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^2.0.0",
|
||||
"mustache": "^3.0.1",
|
||||
"mustache": "^4.0.1",
|
||||
"plugin-error": "^1.0.0",
|
||||
"replace-ext": "^1.0.0",
|
||||
"through2": "^3.0.1"
|
||||
|
@ -3498,12 +3498,12 @@
|
|||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
|
||||
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^3.0.0",
|
||||
"p-locate": "^2.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
|
@ -3877,9 +3877,9 @@
|
|||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.3.tgz",
|
||||
"integrity": "sha512-6uCP4Qc0sWsgMLy1EOqqS/3rjDHOEnsStVr/4vtAIK2Y5i2kA7lFFejYrpIyiN9w0pYf4ckeCYT9f1r1P9KX5g==",
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||
"integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
|
||||
"dev": true
|
||||
},
|
||||
"mocha": {
|
||||
|
@ -3962,9 +3962,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"mustache": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.1.tgz",
|
||||
"integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==",
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.1.tgz",
|
||||
"integrity": "sha512-yL5VE97+OXn4+Er3THSmTdCFCtx5hHWzrolvH+JObZnUYwuaG7XV+Ch4fR2cIrcYI0tFHxS7iyFYl14bW8y2sA==",
|
||||
"dev": true
|
||||
},
|
||||
"mute-stdout": {
|
||||
|
@ -4318,27 +4318,27 @@
|
|||
"dev": true
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.2.tgz",
|
||||
"integrity": "sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ==",
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
|
||||
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
"p-try": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
|
||||
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.0.0"
|
||||
"p-limit": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
|
||||
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
|
||||
"dev": true
|
||||
},
|
||||
"pako": {
|
||||
|
@ -4525,12 +4525,12 @@
|
|||
}
|
||||
},
|
||||
"pkg-up": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
|
||||
"integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-2.0.0.tgz",
|
||||
"integrity": "sha1-yBmscoBZpGHKscOImivjxJoATX8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"find-up": "^3.0.0"
|
||||
"find-up": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"plugin-error": {
|
||||
|
@ -4993,9 +4993,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.15.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
|
||||
"integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
|
||||
"version": "1.16.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.0.tgz",
|
||||
"integrity": "sha512-LarL/PIKJvc09k1jaeT4kQb/8/7P+qV4qSnN2K80AES+OHdfZELAKVOBjxsvtToT/uLOfFbvYvKfZmV8cee7nA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
|
@ -5052,9 +5052,9 @@
|
|||
}
|
||||
},
|
||||
"rxjs": {
|
||||
"version": "7.0.0-alpha.1",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.0.0-alpha.1.tgz",
|
||||
"integrity": "sha512-wQB1ZRQysSXSXzPHhhsw+OaVmNnVSylgR3UkUvVEfexkDmfHEs9bpDz1oiMfIG9JL1hPXL1ABtrL0H6XhstbWg==",
|
||||
"version": "7.0.0-beta.0",
|
||||
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.0.0-beta.0.tgz",
|
||||
"integrity": "sha512-MMsqDczs2RzsTvBiH6SKjJkdAh7WaI6Q0axP/DX+1ljwFm6+18AhQ3kVT8gD7G0dHIVfh5hDFoqLaW79pkiGag==",
|
||||
"requires": {
|
||||
"tslib": "^1.9.0"
|
||||
}
|
||||
|
@ -5165,9 +5165,9 @@
|
|||
}
|
||||
},
|
||||
"shadow-cljs": {
|
||||
"version": "2.8.94",
|
||||
"resolved": "https://registry.npmjs.org/shadow-cljs/-/shadow-cljs-2.8.94.tgz",
|
||||
"integrity": "sha512-o3ykB9TpCnMGem4cjmbAkVeYe/saGMVl6RhC/BdO1UNlhPZUp3hwdVp9mBDN079O5jJEtC8RmjHY6xe9utYokw==",
|
||||
"version": "2.8.95",
|
||||
"resolved": "https://registry.npmjs.org/shadow-cljs/-/shadow-cljs-2.8.95.tgz",
|
||||
"integrity": "sha512-2y5wgD3Bjbtu9hX6kRf7fzWHDga5BzW9CWU+eTrwddX0B2XbcoIKOiHYQTj6QrFFqYc1vkWGZLyYE9kEYN8N0A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"node-libs-browser": "^2.0.0",
|
||||
|
@ -5597,24 +5597,46 @@
|
|||
"strip-ansi": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimleft": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz",
|
||||
"integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==",
|
||||
"string.prototype.trimend": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
|
||||
"integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"function-bind": "^1.1.1"
|
||||
"es-abstract": "^1.17.5"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimleft": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz",
|
||||
"integrity": "sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.17.5",
|
||||
"string.prototype.trimstart": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimright": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz",
|
||||
"integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==",
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz",
|
||||
"integrity": "sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"function-bind": "^1.1.1"
|
||||
"es-abstract": "^1.17.5",
|
||||
"string.prototype.trimend": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"string.prototype.trimstart": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
|
||||
"integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"define-properties": "^1.1.3",
|
||||
"es-abstract": "^1.17.5"
|
||||
}
|
||||
},
|
||||
"string_decoder": {
|
||||
|
@ -5721,6 +5743,15 @@
|
|||
"wrap-ansi": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"find-up": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
|
||||
"integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"locate-path": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"invert-kv": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz",
|
||||
|
@ -5742,15 +5773,31 @@
|
|||
"invert-kv": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"locate-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
|
||||
"integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-locate": "^3.0.0",
|
||||
"path-exists": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
|
||||
"integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
},
|
||||
"mustache": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-3.2.1.tgz",
|
||||
"integrity": "sha512-RERvMFdLpaFfSRIEe632yDm5nsd0SDKn8hGmcUwswnyiE5mtdZLDybtHAz6hjJhawokF0hXvGLtx9mrQfm6FkA==",
|
||||
"dev": true
|
||||
},
|
||||
"os-locale": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz",
|
||||
|
@ -5762,6 +5809,30 @@
|
|||
"mem": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"p-limit": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
|
||||
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-try": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-locate": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
|
||||
"integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"p-limit": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"p-try": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",
|
||||
|
@ -5841,9 +5912,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"mkdirp": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.4.tgz",
|
||||
"integrity": "sha512-iG9AK/dJLtJ0XNgTuDbSyNS3zECqDlAhnQW4CsNxBG3LQJBbHmRX1egw39DmtOdCAqY+dKXV+sgPgilNWUKMVw==",
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
|
|
|
@ -13,22 +13,22 @@
|
|||
],
|
||||
"scripts": {},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^9.7.4",
|
||||
"autoprefixer": "^9.7.6",
|
||||
"clean-css": "^4.2.3",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-gzip": "^1.4.2",
|
||||
"gulp-if": "^3.0.0",
|
||||
"gulp-mustache": "^4.1.2",
|
||||
"gulp-mustache": "^5.0.0",
|
||||
"gulp-rename": "^2.0.0",
|
||||
"gulp-svg-sprite": "^1.5.0",
|
||||
"mkdirp": "^1.0.3",
|
||||
"mkdirp": "^1.0.4",
|
||||
"postcss": "^7.0.27",
|
||||
"rimraf": "^3.0.0",
|
||||
"sass": "^1.26.0",
|
||||
"shadow-cljs": "^2.8.94"
|
||||
"shadow-cljs": "^2.8.95"
|
||||
},
|
||||
"dependencies": {
|
||||
"date-fns": "^2.11.1",
|
||||
"date-fns": "^2.12.0",
|
||||
"mousetrap": "^1.6.5",
|
||||
"randomcolor": "^0.5.4",
|
||||
"react": "^16.13.1",
|
||||
|
@ -36,7 +36,7 @@
|
|||
"react-dnd": "^10.0.2",
|
||||
"react-dnd-html5-backend": "^10.0.2",
|
||||
"react-dom": "^16.13.1",
|
||||
"rxjs": "^7.0.0-alpha.1",
|
||||
"rxjs": "^7.0.0-beta.0",
|
||||
"source-map-support": "^0.5.16",
|
||||
"tdigest": "^0.1.1",
|
||||
"xregexp": "^4.3.0"
|
||||
|
|
|
@ -7,16 +7,15 @@
|
|||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns uxbox.main.data.helpers)
|
||||
(ns uxbox.main.data.helpers
|
||||
(:require [uxbox.common.data :as d]))
|
||||
|
||||
(defn get-children
|
||||
"Retrieve all children ids recursively for a given shape"
|
||||
[shape-id objects]
|
||||
(let [shapes (get-in objects [shape-id :shapes])]
|
||||
(if shapes
|
||||
(concat
|
||||
shapes
|
||||
(mapcat #(get-children % objects) shapes))
|
||||
(d/concat shapes (mapcat #(get-children % objects) shapes))
|
||||
[])))
|
||||
|
||||
(defn is-shape-grouped
|
||||
|
@ -29,7 +28,7 @@
|
|||
(some contains-shape-fn shapes)))
|
||||
|
||||
(defn get-parent
|
||||
"Retrieve the id of the parent for the shape-id (if exists"
|
||||
"Retrieve the id of the parent for the shape-id (if exists)"
|
||||
[shape-id objects]
|
||||
(let [check-parenthood
|
||||
(fn [shape] (when (and (:shapes shape)
|
||||
|
@ -55,9 +54,10 @@
|
|||
(rec-fn shape-id [])))
|
||||
|
||||
(defn replace-shapes
|
||||
"Replace inside shapes the value `to-replace-id` for the value in items keeping the same order.
|
||||
`to-replace-id` can be a set, a sequable or a single value. Any of these will be changed into a
|
||||
set to make the replacement"
|
||||
"Replace inside shapes the value `to-replace-id` for the value in
|
||||
items keeping the same order. `to-replace-id` can be a set, a
|
||||
sequable or a single value. Any of these will be changed into a set
|
||||
to make the replacement"
|
||||
[shape to-replace-id items]
|
||||
(let [should-replace
|
||||
(cond
|
||||
|
|
|
@ -9,21 +9,22 @@
|
|||
|
||||
(ns uxbox.main.data.workspace
|
||||
(:require
|
||||
[clojure.set :as set]
|
||||
[beicon.core :as rx]
|
||||
[goog.object :as gobj]
|
||||
[goog.events :as events]
|
||||
[cljs.spec.alpha :as s]
|
||||
[clojure.set :as set]
|
||||
[goog.events :as events]
|
||||
[goog.object :as gobj]
|
||||
[potok.core :as ptk]
|
||||
[uxbox.common.data :as d]
|
||||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.common.spec :as us]
|
||||
[uxbox.common.exceptions :as ex]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.config :as cfg]
|
||||
[uxbox.main.constants :as c]
|
||||
[uxbox.main.data.icons :as udi]
|
||||
[uxbox.main.data.dashboard :as dd]
|
||||
[uxbox.main.data.helpers :as helpers]
|
||||
[uxbox.main.data.icons :as udi]
|
||||
[uxbox.main.geom :as geom]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.repo :as rp]
|
||||
|
@ -38,12 +39,7 @@
|
|||
[uxbox.util.router :as rt]
|
||||
[uxbox.util.time :as dt]
|
||||
[uxbox.util.transit :as t]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.util.webapi :as wapi]
|
||||
#_[vendor.randomcolor])
|
||||
(:import goog.events.EventType
|
||||
goog.events.KeyCodes
|
||||
goog.ui.KeyboardShortcutHandler))
|
||||
[uxbox.util.webapi :as wapi]))
|
||||
|
||||
;; TODO: temporal workaround
|
||||
(def clear-ruler nil)
|
||||
|
@ -52,8 +48,11 @@
|
|||
;; --- Specs
|
||||
|
||||
(s/def ::shape-attrs ::cp/shape-attrs)
|
||||
|
||||
(s/def ::set-of-uuid
|
||||
(s/every uuid? :kind set?))
|
||||
(s/def ::set-of-string
|
||||
(s/every string? :kind set?))
|
||||
|
||||
;; --- Expose inner functions
|
||||
|
||||
|
@ -368,7 +367,9 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [local (:workspace-local state)]
|
||||
(assoc-in state [:workspace-cache page-id] local)))))
|
||||
(-> state
|
||||
(assoc-in [:workspace-cache page-id] local)
|
||||
(update :workspace-data dissoc page-id))))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
@ -525,7 +526,10 @@
|
|||
(->> (rp/query :file-with-users {:id id})
|
||||
(rx/merge-map (fn [result]
|
||||
(rx/of (file-fetched (dissoc result :users))
|
||||
(users-fetched (:users result)))))))))
|
||||
(users-fetched (:users result)))))
|
||||
(rx/catch (fn [{:keys [type] :as error}]
|
||||
(when (= :not-found type)
|
||||
(rx/of (rt/nav :not-found)))))))))
|
||||
(defn fetch-file
|
||||
[id]
|
||||
(us/verify ::us/uuid id)
|
||||
|
@ -860,24 +864,6 @@
|
|||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :zoom] 2))))
|
||||
|
||||
;; --- Grid Alignment
|
||||
|
||||
;; (defn initialize-alignment
|
||||
;; [id]
|
||||
;; (us/verify ::us/uuid id)
|
||||
;; (ptk/reify ::initialize-alignment
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state stream]
|
||||
;; (let [metadata (get-in state [:workspace-page :metadata])
|
||||
;; params {:width c/viewport-width
|
||||
;; :height c/viewport-height
|
||||
;; :x-axis (:grid-x-axis metadata c/grid-x-axis)
|
||||
;; :y-axis (:grid-y-axis metadata c/grid-y-axis)}]
|
||||
;; (rx/concat
|
||||
;; (rx/of (deactivate-flag :grid-indexed))
|
||||
;; (->> (uwrk/initialize-alignment params)
|
||||
;; (rx/map #(activate-flag :grid-indexed))))))))
|
||||
|
||||
;; --- Selection Rect
|
||||
|
||||
(declare select-shapes-by-current-selrect)
|
||||
|
@ -928,44 +914,63 @@
|
|||
;; Shapes events
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; --- Toggle shape's selection status (selected or deselected)
|
||||
|
||||
(defn select-shape
|
||||
[id]
|
||||
(us/verify ::us/uuid id)
|
||||
(ptk/reify ::select-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-local :selected]
|
||||
(fn [selected]
|
||||
(if (contains? selected id)
|
||||
(disj selected id)
|
||||
(conj selected id)))))))
|
||||
|
||||
(defn select-shapes
|
||||
[ids]
|
||||
(us/verify ::set-of-uuid ids)
|
||||
(ptk/reify ::select-shapes
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-local :selected] ids))))
|
||||
|
||||
(def deselect-all
|
||||
"Clear all possible state of drawing, edition
|
||||
or any similar action taken by the user."
|
||||
(ptk/reify ::deselect-all
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-local #(-> %
|
||||
(assoc :selected #{})
|
||||
(dissoc :selected-frame))))))
|
||||
|
||||
|
||||
;; --- Add shape to Workspace
|
||||
|
||||
(defn impl-retrieve-used-names
|
||||
(defn- retrieve-used-names
|
||||
[objects]
|
||||
(into #{} (map :name) (vals objects)))
|
||||
|
||||
(defn extract-numeric-suffix
|
||||
(defn- extract-numeric-suffix
|
||||
[basename]
|
||||
(if-let [[match p1 p2] (re-find #"(.*)-([0-9]+)$" basename)]
|
||||
[p1 (+ 1 (d/parse-integer p2))]
|
||||
[basename 1]))
|
||||
|
||||
(defn impl-generate-unique-name
|
||||
(defn- generate-unique-name
|
||||
"A unique name generator"
|
||||
[objects basename]
|
||||
(let [used (impl-retrieve-used-names objects)
|
||||
[prefix initial] (extract-numeric-suffix basename)]
|
||||
[used basename]
|
||||
(s/assert ::set-of-string used)
|
||||
(s/assert ::us/string basename)
|
||||
(let [[prefix initial] (extract-numeric-suffix basename)]
|
||||
(loop [counter initial]
|
||||
(let [candidate (str prefix "-" counter)]
|
||||
(if (contains? used candidate)
|
||||
(recur (inc counter))
|
||||
candidate)))))
|
||||
|
||||
(defn impl-assoc-shape
|
||||
"Add a shape to the current workspace page, inside a given frame.
|
||||
Give it a name that is unique in the page"
|
||||
[state {:keys [id frame-id] :as data}]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
name (impl-generate-unique-name objects (:name data))
|
||||
shape (assoc data :name name)
|
||||
page-id (::page-id state)]
|
||||
(-> state
|
||||
(update-in [:workspace-data page-id :objects frame-id :shapes] conj id)
|
||||
(update-in [:workspace-data page-id :objects] assoc id shape))))
|
||||
|
||||
(declare select-shape)
|
||||
|
||||
(defn- calculate-frame-overlap
|
||||
[objects shape]
|
||||
(let [rshp (geom/shape->rect-shape shape)
|
||||
|
@ -985,172 +990,161 @@
|
|||
(defn add-shape
|
||||
[attrs]
|
||||
(us/verify ::shape-attrs attrs)
|
||||
(let [id (uuid/next)]
|
||||
(ptk/reify ::add-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
shape (-> (geom/setup-proportions attrs)
|
||||
(assoc :id id))
|
||||
frame-id (calculate-frame-overlap objects shape)
|
||||
shape (merge cp/default-shape-attrs shape {:frame-id frame-id})]
|
||||
(-> state
|
||||
(impl-assoc-shape shape)
|
||||
(assoc-in [:workspace-local :selected] #{id}))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (::page-id state)
|
||||
obj (get-in state [:workspace-data page-id :objects id])]
|
||||
(rx/of (commit-changes [{:type :add-obj
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
|
||||
id (uuid/next)
|
||||
shape (geom/setup-proportions attrs)
|
||||
|
||||
unames (retrieve-used-names objects)
|
||||
name (generate-unique-name unames (:name shape))
|
||||
|
||||
frame-id (if (= :frame (:type shape))
|
||||
uuid/zero
|
||||
(calculate-frame-overlap objects shape))
|
||||
|
||||
shape (merge
|
||||
(if (= :frame (:type shape))
|
||||
cp/default-frame-attrs
|
||||
cp/default-shape-attrs)
|
||||
(assoc shape
|
||||
:id id
|
||||
:frame-id (:frame-id obj)
|
||||
:obj obj}]
|
||||
[{:type :del-obj
|
||||
:id id}])))))))
|
||||
|
||||
(defn add-frame
|
||||
[data]
|
||||
(us/verify ::shape-attrs data)
|
||||
(let [id (uuid/next)]
|
||||
(ptk/reify ::add-frame
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [shape (-> (geom/setup-proportions data)
|
||||
(assoc :id id))
|
||||
shape (merge cp/default-frame-attrs shape)]
|
||||
(impl-assoc-shape state shape)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (::page-id state)
|
||||
obj (get-in state [:workspace-data page-id :objects id])]
|
||||
(rx/of (commit-changes [{:type :add-obj
|
||||
:id id
|
||||
:frame-id (:frame-id obj)
|
||||
:obj obj}]
|
||||
[{:type :del-obj
|
||||
:id id}])))))))
|
||||
|
||||
|
||||
;; --- Duplicate Selected
|
||||
;; TODO: handle properly naming
|
||||
|
||||
(defn duplicate-shapes
|
||||
[shapes]
|
||||
(ptk/reify ::duplicate-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
rchanges (mapv (fn [id]
|
||||
(let [obj (assoc (get objects id)
|
||||
:id (uuid/next))]
|
||||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
:frame-id (:frame-id obj)
|
||||
:obj obj
|
||||
:session-id (:session-id state)}))
|
||||
shapes)
|
||||
uchanges (mapv (fn [rch]
|
||||
{:type :del-obj
|
||||
:id (:id rch)
|
||||
:session-id (:session-id state)})
|
||||
rchanges)]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn duplicate-frame
|
||||
[frame-id]
|
||||
(ptk/reify ::duplicate-frame
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
|
||||
frame (get objects frame-id)
|
||||
frame-id (uuid/next)
|
||||
|
||||
rchanges (mapv (fn [id]
|
||||
(let [obj (assoc (get objects id)
|
||||
:id (uuid/next))]
|
||||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
:frame-id frame-id
|
||||
:obj (assoc obj :frame-id frame-id)}))
|
||||
(:shapes frame))
|
||||
|
||||
uchanges (mapv (fn [rch]
|
||||
{:type :del-obj
|
||||
:id (:id rch)})
|
||||
rchanges)
|
||||
|
||||
shapes (mapv :id rchanges)
|
||||
:name name
|
||||
:frame-id frame-id))
|
||||
|
||||
rchange {:type :add-obj
|
||||
:id id
|
||||
:frame-id frame-id
|
||||
:obj shape}
|
||||
uchange {:type :del-obj
|
||||
:id id}]
|
||||
|
||||
(rx/of (commit-changes [rchange] [uchange] {:commit-local? true})
|
||||
(select-shapes #{id}))))))
|
||||
|
||||
|
||||
;; --- Duplicate Shapes
|
||||
|
||||
(declare prepare-duplicate-changes)
|
||||
(declare prepare-duplicate-change)
|
||||
(declare prepare-duplicate-frame-change)
|
||||
(declare prepare-duplicate-shape-change)
|
||||
|
||||
(def ^:private change->name #(get-in % [:obj :name]))
|
||||
|
||||
(defn- prepare-duplicate-changes
|
||||
"Prepare objects to paste: generate new id, give them unique names,
|
||||
move to the position of mouse pointer, and find in what frame they
|
||||
fit."
|
||||
[objects names ids delta]
|
||||
(loop [names names
|
||||
chgs []
|
||||
id (first ids)
|
||||
ids (rest ids)]
|
||||
(if (nil? id)
|
||||
chgs
|
||||
(let [result (prepare-duplicate-change objects names id delta)
|
||||
result (if (vector? result) result [result])]
|
||||
(recur
|
||||
(into names (map change->name) result)
|
||||
(into chgs result)
|
||||
(first ids)
|
||||
(rest ids))))))
|
||||
|
||||
(defn- prepare-duplicate-change
|
||||
[objects names id delta]
|
||||
(let [obj (get objects id)]
|
||||
(if (= :frame (:type obj))
|
||||
(prepare-duplicate-frame-change objects names obj delta)
|
||||
(prepare-duplicate-shape-change objects names obj delta nil nil))))
|
||||
|
||||
(defn- prepare-duplicate-shape-change
|
||||
[objects names obj delta frame-id parent-id]
|
||||
(let [id (uuid/next)
|
||||
name (generate-unique-name names (:name obj))
|
||||
renamed-obj (assoc obj :id id :name name)
|
||||
moved-obj (geom/move renamed-obj delta)
|
||||
frame-id (if frame-id
|
||||
frame-id
|
||||
(calculate-frame-overlap objects moved-obj))
|
||||
|
||||
parent-id (or parent-id frame-id)
|
||||
|
||||
children-changes
|
||||
(loop [names names
|
||||
result []
|
||||
cid (first (:shapes obj))
|
||||
cids (rest (:shapes obj))]
|
||||
(if (nil? cid)
|
||||
result
|
||||
(let [obj (get objects cid)
|
||||
changes (prepare-duplicate-shape-change objects names obj delta frame-id id)]
|
||||
(recur
|
||||
(into names (map change->name changes))
|
||||
(into result changes)
|
||||
(first cids)
|
||||
(rest cids)))))
|
||||
|
||||
reframed-obj (-> moved-obj
|
||||
(assoc :frame-id frame-id)
|
||||
(dissoc :shapes))]
|
||||
(into [{:type :add-obj
|
||||
:id id
|
||||
:old-id (:id obj)
|
||||
:frame-id frame-id
|
||||
:parent-id parent-id
|
||||
:obj (dissoc reframed-obj :shapes)}]
|
||||
children-changes)))
|
||||
|
||||
(defn- prepare-duplicate-frame-change
|
||||
[objects names obj delta]
|
||||
(let [frame-id (uuid/next)
|
||||
frame-name (generate-unique-name names (:name obj))
|
||||
sch (->> (map #(get objects %) (:shapes obj))
|
||||
(mapcat #(prepare-duplicate-shape-change objects names % delta frame-id frame-id)))
|
||||
|
||||
renamed-frame (-> obj
|
||||
(assoc :id frame-id)
|
||||
(assoc :name frame-name)
|
||||
(assoc :frame-id uuid/zero)
|
||||
(dissoc :shapes))
|
||||
|
||||
moved-frame (geom/move renamed-frame delta)
|
||||
|
||||
fch {:type :add-obj
|
||||
:old-id (:id obj)
|
||||
:id frame-id
|
||||
:frame-id uuid/zero
|
||||
:obj (assoc frame
|
||||
:id frame-id
|
||||
:shapes shapes)
|
||||
:obj moved-frame}]
|
||||
|
||||
:session-id (:session-id state)}
|
||||
|
||||
uchange {:type :del-obj
|
||||
:id frame-id
|
||||
:session-id (:session-id state)}]
|
||||
(rx/of (commit-changes (d/concat [rchange] rchanges)
|
||||
(d/concat [] uchanges [uchange])
|
||||
{:commit-local? true}))))))
|
||||
(into [fch] sch)))
|
||||
|
||||
(declare select-shapes)
|
||||
|
||||
(def duplicate-selected
|
||||
(ptk/reify ::duplicate-selected
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
lookup #(get objects %)
|
||||
shapes (map lookup selected)
|
||||
shape? #(not= (:type %) :frame)]
|
||||
(cond
|
||||
(and (= (count shapes) 1)
|
||||
(= (:type (first shapes)) :frame))
|
||||
(rx/of (duplicate-frame (first selected)))
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
delta (gpt/point 0 0)
|
||||
unames (retrieve-used-names objects)
|
||||
|
||||
(and (pos? (count shapes))
|
||||
(every? shape? shapes))
|
||||
(rx/of (duplicate-shapes selected))
|
||||
rchanges (prepare-duplicate-changes objects unames selected delta)
|
||||
uchanges (mapv #(array-map :type :del-obj :id (:id %))
|
||||
(reverse rchanges))
|
||||
|
||||
:else
|
||||
(rx/empty))))))
|
||||
selected (->> rchanges
|
||||
(filter #(selected (:old-id %)))
|
||||
(map #(get-in % [:obj :id]))
|
||||
(into #{}))]
|
||||
|
||||
|
||||
|
||||
;; --- Toggle shape's selection status (selected or deselected)
|
||||
|
||||
(defn select-shape
|
||||
[id]
|
||||
(us/verify ::us/uuid id)
|
||||
(ptk/reify ::select-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-local :selected]
|
||||
(fn [selected]
|
||||
(if (contains? selected id)
|
||||
(disj selected id)
|
||||
(conj selected id)))))))
|
||||
|
||||
(def deselect-all
|
||||
"Clear all possible state of drawing, edition
|
||||
or any similar action taken by the user."
|
||||
(ptk/reify ::deselect-all
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-local #(-> %
|
||||
(assoc :selected #{})
|
||||
(dissoc :selected-frame))))))
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true})
|
||||
(select-shapes selected))))))
|
||||
|
||||
|
||||
;; --- Select Shapes (By selrect)
|
||||
|
@ -1318,30 +1312,30 @@
|
|||
(let [page-id (::page-id state)
|
||||
session-id (:session-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
rchanges (mapv #(array-map :type :del-obj :id %) ids)
|
||||
uchanges (mapv (fn [id]
|
||||
(let [obj (get objects id)
|
||||
frm (get objects (:frame-id obj))
|
||||
idx (d/index-of (:shapes frm) id)]
|
||||
cpindex (helpers/calculate-child-parent-map objects)
|
||||
|
||||
del-change #(array-map :type :del-obj :id %)
|
||||
|
||||
rchanges
|
||||
(reduce (fn [res id]
|
||||
(let [chd (helpers/get-children id objects)]
|
||||
(into res (d/concat
|
||||
(mapv del-change (reverse chd))
|
||||
[(del-change id)]))))
|
||||
[]
|
||||
ids)
|
||||
|
||||
uchanges
|
||||
(mapv (fn [id]
|
||||
(let [obj (get objects id)]
|
||||
{:type :add-obj
|
||||
:id id
|
||||
:frame-id (:id frm)
|
||||
:index idx
|
||||
:frame-id (:frame-id obj)
|
||||
:parent-id (get cpindex id)
|
||||
:obj obj}))
|
||||
(reverse ids))]
|
||||
(reverse (map :id rchanges)))]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))
|
||||
|
||||
(defn- delete-frame
|
||||
[id]
|
||||
(ptk/reify ::delete-shapes
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
obj (get objects id)
|
||||
ids (d/concat [] (:shapes obj) [(:id obj)])]
|
||||
(rx/of (delete-shapes ids))))))
|
||||
|
||||
(def delete-selected
|
||||
"Deselect all and remove all selected shapes."
|
||||
(ptk/reify ::delete-selected
|
||||
|
@ -1353,17 +1347,8 @@
|
|||
|
||||
shapes (map lookup selected)
|
||||
shape? #(not= (:type %) :frame)]
|
||||
(cond
|
||||
(and (= (count shapes) 1)
|
||||
(= (:type (first shapes)) :frame))
|
||||
(rx/of (delete-frame (first selected)))
|
||||
(rx/of (delete-shapes selected))))))
|
||||
|
||||
(and (pos? (count shapes))
|
||||
(every? shape? shapes))
|
||||
(rx/of (delete-shapes selected))
|
||||
|
||||
:else
|
||||
(rx/empty))))))
|
||||
|
||||
;; --- Rename Shape
|
||||
|
||||
|
@ -1417,7 +1402,7 @@
|
|||
(us/verify ::us/uuid ref-id)
|
||||
(us/verify number? index)
|
||||
|
||||
(ptk/reify ::reloacate-shape
|
||||
(ptk/reify ::relocate-shape
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page-id (::page-id state)
|
||||
|
@ -1431,28 +1416,6 @@
|
|||
[]
|
||||
{:commit-local? true}))))))
|
||||
|
||||
(defn commit-shape-order-change
|
||||
[id]
|
||||
(ptk/reify ::commit-shape-order-change
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [pid (::page-id state)
|
||||
obj (get-in state [:workspace-data pid :objects id])
|
||||
|
||||
cfrm (get-in state [:workspace-data pid :objects (:frame-id obj)])
|
||||
pfrm (get-in state [:pages-data pid :objects (:frame-id obj)])
|
||||
|
||||
cindex (d/index-of (:shapes cfrm) id)
|
||||
pindex (d/index-of (:shapes pfrm) id)
|
||||
|
||||
rchange {:type :mod-obj
|
||||
:id (:id cfrm)
|
||||
:operations [{:type :abs-order :id id :index cindex}]}
|
||||
uchange {:type :mod-obj
|
||||
:id (:id cfrm)
|
||||
:operations [{:type :abs-order :id id :index pindex}]}]
|
||||
(rx/of (commit-changes [rchange] [uchange]))))))
|
||||
|
||||
|
||||
;; --- Shape / Selection Alignment and Distribution
|
||||
|
||||
|
@ -1505,40 +1468,64 @@
|
|||
|
||||
;; --- Temportal displacement for Shape / Selection
|
||||
|
||||
(defn- rehash-shape-frame-relationship
|
||||
[ids]
|
||||
(letfn [(impl-diff [state]
|
||||
(defn- retrieve-toplevel-shapes
|
||||
[objects]
|
||||
(let [lookup #(get objects %)
|
||||
root (lookup uuid/zero)
|
||||
childs (:shapes root)]
|
||||
(loop [id (first childs)
|
||||
ids (rest childs)
|
||||
res []]
|
||||
(if (nil? id)
|
||||
res
|
||||
(let [obj (lookup id)
|
||||
typ (:type obj)]
|
||||
(recur (first ids)
|
||||
(rest ids)
|
||||
(if (= :frame typ)
|
||||
(into res (:shapes obj))
|
||||
(conj res id))))))))
|
||||
|
||||
(defn- calculate-shape-to-frame-relationship-changes
|
||||
[objects ids]
|
||||
(loop [id (first ids)
|
||||
ids (rest ids)
|
||||
rch []
|
||||
uch []]
|
||||
(if (nil? id)
|
||||
[rch uch]
|
||||
(let [pid (::page-id state)
|
||||
objects (get-in state [:workspace-data pid :objects])
|
||||
obj (get objects id)
|
||||
(let [obj (get objects id)
|
||||
fid (calculate-frame-overlap objects obj)]
|
||||
(if (not= fid (:frame-id obj))
|
||||
(recur (first ids)
|
||||
(rest ids)
|
||||
(conj rch {:type :mov-obj
|
||||
:id id
|
||||
:frame-id fid})
|
||||
(conj uch {:type :mov-obj
|
||||
:id id
|
||||
:frame-id (:frame-id obj)}))
|
||||
(conj rch {:type :mov-objects
|
||||
:parent-id fid
|
||||
:shapes [id]})
|
||||
(conj uch {:type :mov-objects
|
||||
:parent-id (:frame-id obj)
|
||||
:shapes [id]}))
|
||||
(recur (first ids)
|
||||
(rest ids)
|
||||
rch
|
||||
uch))))))]
|
||||
uch))))))
|
||||
|
||||
(defn- rehash-shape-frame-relationship
|
||||
[ids]
|
||||
(ptk/reify ::rehash-shape-frame-relationship
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [[rch uch] (impl-diff state)]
|
||||
(when-not (empty? rch)
|
||||
(rx/of (commit-changes rch uch {:commit-local? true}))))))))
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
ids (retrieve-toplevel-shapes objects)
|
||||
[rch uch] (calculate-shape-to-frame-relationship-changes objects ids)
|
||||
]
|
||||
|
||||
(defn- adjust-group-shapes [state ids]
|
||||
(when-not (empty? rch)
|
||||
(rx/of (commit-changes rch uch {:commit-local? true})))))))
|
||||
|
||||
(defn- adjust-group-shapes
|
||||
[state ids]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
groups-to-adjust (->> ids
|
||||
|
@ -1944,25 +1931,6 @@
|
|||
(let [page-id (::page-id state)]
|
||||
(update-in state [:workspace-data page-id :objects id :segments index] gpt/add delta)))))
|
||||
|
||||
;; --- Initial Path Point Alignment
|
||||
|
||||
;; ;; TODO: revisit on alignemt refactor
|
||||
;; (deftype InitialPathPointAlign [id index]
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state s]
|
||||
;; (let [shape (get-in state [:workspace-data :objects id])
|
||||
;; point (get-in shape [:segments index])]
|
||||
;; (->> (uwrk/align-point point)
|
||||
;; (rx/map #(update-path id index %))))))
|
||||
|
||||
;; (defn initial-path-point-align
|
||||
;; "Event responsible of align a specified point of the
|
||||
;; shape by `index` with the grid."
|
||||
;; [id index]
|
||||
;; {:pre [(uuid? id)
|
||||
;; (number? index)
|
||||
;; (not (neg? index))]}
|
||||
;; (InitialPathPointAlign. id index))
|
||||
|
||||
;; --- Shape Visibility
|
||||
|
||||
|
@ -2137,18 +2105,17 @@
|
|||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def copy-selected
|
||||
(letfn [(prepare-selected [state selected]
|
||||
(let [data (reduce #(prepare %1 state %2) {} selected)]
|
||||
(letfn [(prepare-selected [objects selected]
|
||||
(let [data (reduce #(prepare %1 objects %2) {} selected)]
|
||||
{:type :copied-shapes
|
||||
:data (assoc data :selected selected)}))
|
||||
:selected selected
|
||||
:objects data}))
|
||||
|
||||
(prepare [result state id]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
object (get objects id)]
|
||||
(cond-> (assoc-in result [:objects id] object)
|
||||
(= :frame (:type object))
|
||||
(as-> $ (reduce #(prepare %1 state %2) $ (:shapes object))))))
|
||||
(prepare [result objects id]
|
||||
(let [obj (get objects id)]
|
||||
(as-> result $$
|
||||
(assoc $$ id obj)
|
||||
(reduce #(prepare %1 objects %2) $$ (:shapes obj)))))
|
||||
|
||||
(on-copy-error [error]
|
||||
(js/console.error "Clipboard blocked:" error)
|
||||
|
@ -2157,98 +2124,18 @@
|
|||
(ptk/reify ::copy-selected
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:workspace-local :selected])
|
||||
cdata (prepare-selected state selected)]
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
cdata (prepare-selected objects selected)]
|
||||
(->> (t/encode cdata)
|
||||
(wapi/write-to-clipboard)
|
||||
(rx/from)
|
||||
(rx/catch on-copy-error)
|
||||
(rx/ignore)))))))
|
||||
|
||||
|
||||
(declare select-pasted-objs)
|
||||
|
||||
(defn- paste-impl
|
||||
[{:keys [selected objects] :as data}]
|
||||
(letfn [(prepare-changes [state delta]
|
||||
"Prepare objects to paste: generate new id, give them unique names, move
|
||||
to the position of mouse pointer, and find in what frame they fit."
|
||||
(let [page-id (::page-id state)]
|
||||
(loop [existing-objs (get-in state [:workspace-data page-id :objects])
|
||||
chgs []
|
||||
id (first selected)
|
||||
ids (rest selected)]
|
||||
(if (nil? id)
|
||||
chgs
|
||||
(let [result (prepare-change id existing-objs delta)
|
||||
result (if (vector? result) result [result])]
|
||||
(recur
|
||||
(reduce #(assoc %1 (:id %2) (:obj %2)) existing-objs result)
|
||||
(into chgs result)
|
||||
(first ids)
|
||||
(rest ids)))))))
|
||||
|
||||
(prepare-change [id existing-objs delta]
|
||||
(let [obj (get objects id)]
|
||||
(if (= :frame (:type obj))
|
||||
(prepare-frame-change existing-objs obj delta)
|
||||
(prepare-shape-change existing-objs obj delta nil))))
|
||||
|
||||
(prepare-shape-change [objects obj delta frame-id]
|
||||
(let [id (uuid/next)
|
||||
name (impl-generate-unique-name objects (:name obj))
|
||||
renamed-obj (assoc obj :id id :name name)
|
||||
moved-obj (geom/move renamed-obj delta)
|
||||
frame-id (if frame-id
|
||||
frame-id
|
||||
(calculate-frame-overlap objects moved-obj))
|
||||
|
||||
prepare-child
|
||||
(fn [child-id]
|
||||
(prepare-shape-change objects (get objects child-id) delta frame-id))
|
||||
|
||||
children-changes (mapcat prepare-child (:shapes obj))
|
||||
is-child? (set (:shapes obj))
|
||||
children-uuids (->> children-changes
|
||||
(filter #(and (= :add-obj (:type %)) (is-child? (:old-id %))))
|
||||
(map #(:id %)))
|
||||
|
||||
move-change (when (not (empty? (:shapes obj)))
|
||||
[{:type :mov-objects
|
||||
:parent-id id
|
||||
:shapes (vec children-uuids)}])
|
||||
|
||||
reframed-obj (-> moved-obj
|
||||
(assoc :frame-id frame-id)
|
||||
(dissoc :shapes))]
|
||||
(into [{:type :add-obj
|
||||
:id id
|
||||
:old-id (:id obj)
|
||||
:frame-id frame-id
|
||||
:obj (dissoc reframed-obj :shapes)}]
|
||||
(concat children-changes move-change))))
|
||||
|
||||
(prepare-frame-change [objects obj delta]
|
||||
(let [frame-id (uuid/next)
|
||||
frame-name (impl-generate-unique-name objects (:name obj))
|
||||
sch (->> (map #(get objects %) (:shapes obj))
|
||||
(mapcat #(prepare-shape-change objects % delta frame-id)))
|
||||
is-child? (set (:shapes obj))
|
||||
children-uuids (->> sch
|
||||
(filter #(and (= :add-obj (:type %)) (is-child? (:old-id %))))
|
||||
(map #(:id %)))
|
||||
renamed-frame (-> obj
|
||||
(assoc :id frame-id)
|
||||
(assoc :name frame-name)
|
||||
(assoc :frame-id uuid/zero)
|
||||
(assoc :shapes (vec children-uuids)))
|
||||
moved-frame (geom/move renamed-frame delta)
|
||||
fch {:type :add-obj
|
||||
:id frame-id
|
||||
:frame-id uuid/zero
|
||||
:obj moved-frame}]
|
||||
(into [fch] sch)))]
|
||||
|
||||
(ptk/reify ::paste-impl
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
@ -2258,15 +2145,20 @@
|
|||
mouse-pos @ms/mouse-position
|
||||
delta (gpt/subtract mouse-pos orig-pos)
|
||||
|
||||
rchanges (prepare-changes state delta)
|
||||
uchanges (map (fn [ch]
|
||||
{:type :del-obj
|
||||
:id (:id ch)})
|
||||
(filter #(= :add-obj (:type %)) rchanges))]
|
||||
(rx/of (commit-changes (vec rchanges)
|
||||
(vec (reverse uchanges))
|
||||
{:commit-local? true})
|
||||
(select-pasted-objs selected rchanges)))))))
|
||||
page-id (::page-id state)
|
||||
unames (-> (get-in state [:workspace-data page-id :objects])
|
||||
(retrieve-used-names))
|
||||
|
||||
rchanges (prepare-duplicate-changes objects unames selected delta)
|
||||
uchanges (mapv #(array-map :type :del-obj :id (:id %))
|
||||
(reverse rchanges))
|
||||
|
||||
selected (->> rchanges
|
||||
(filter #(selected (:old-id %)))
|
||||
(map #(get-in % [:obj :id]))
|
||||
(into #{}))]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true})
|
||||
(select-shapes selected))))))
|
||||
|
||||
(def paste
|
||||
(ptk/reify ::paste
|
||||
|
@ -2275,23 +2167,12 @@
|
|||
(->> (rx/from (wapi/read-from-clipboard))
|
||||
(rx/map t/decode)
|
||||
(rx/filter #(= :copied-shapes (:type %)))
|
||||
(rx/pr-log "pasting:")
|
||||
(rx/map :data)
|
||||
(rx/map #(select-keys % [:selected :objects]))
|
||||
(rx/map paste-impl)
|
||||
(rx/catch (fn [err]
|
||||
(js/console.error "Clipboard error:" err)
|
||||
(rx/empty)))))))
|
||||
|
||||
(defn select-pasted-objs
|
||||
[selected rchanges]
|
||||
(ptk/reify ::select-pasted-objs
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [new-selected (->> rchanges
|
||||
(filter #(selected (:old-id %)))
|
||||
(map #(get-in % [:obj :id]))
|
||||
(into #{}))]
|
||||
(assoc-in state [:workspace-local :selected] new-selected)))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Page Changes Reactions
|
||||
|
@ -2317,14 +2198,8 @@
|
|||
;; GROUPS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn get-parent [object-id objects]
|
||||
(let [include-object
|
||||
(fn [object]
|
||||
(and (:shapes object)
|
||||
(some #(= object-id %) (:shapes object))))]
|
||||
(first (filter include-object objects))))
|
||||
|
||||
(defn group-shape [id frame-id selected selection-rect]
|
||||
(defn group-shape
|
||||
[id frame-id selected selection-rect]
|
||||
{:id id
|
||||
:type :group
|
||||
:name (name (gensym "Group-"))
|
||||
|
@ -2335,13 +2210,13 @@
|
|||
:width (:width selection-rect)
|
||||
:height (:height selection-rect)})
|
||||
|
||||
(defn create-group []
|
||||
(let [id (uuid/next)]
|
||||
(def create-group
|
||||
(ptk/reify ::create-group
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:workspace-local :selected])]
|
||||
(if (not-empty selected)
|
||||
(let [id (uuid/next)
|
||||
selected (get-in state [:workspace-local :selected])]
|
||||
(when (not-empty selected)
|
||||
(let [page-id (get-in state [:workspace-page :id])
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
selected-objects (map (partial get objects) selected)
|
||||
|
@ -2349,43 +2224,46 @@
|
|||
frame-id (-> selected-objects first :frame-id)
|
||||
group-shape (group-shape id frame-id selected selection-rect)
|
||||
frame-children (get-in objects [frame-id :shapes])
|
||||
index-frame (->> frame-children (map-indexed vector) (filter #(selected (second %))) first first)]
|
||||
index-frame (->> frame-children
|
||||
(map-indexed vector)
|
||||
(filter #(selected (second %)))
|
||||
(ffirst))
|
||||
|
||||
(let [rchanges [{:type :add-obj
|
||||
rchanges [{:type :add-obj
|
||||
:id id
|
||||
:frame-id frame-id
|
||||
:obj group-shape
|
||||
:index index-frame}
|
||||
{:type :mov-objects
|
||||
:parent-id id
|
||||
:shapes (into [] selected)}]
|
||||
:shapes (vec selected)}]
|
||||
uchanges [{:type :mov-objects
|
||||
:parent-id frame-id
|
||||
:shapes (into [] selected)}
|
||||
:shapes (vec selected)}
|
||||
{:type :del-obj
|
||||
:id id}]]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true})
|
||||
(fn [state] (assoc-in state [:workspace-local :selected] #{id})))))
|
||||
rx/empty))))))
|
||||
(select-shapes #{id}))))))))
|
||||
|
||||
(defn remove-group
|
||||
[]
|
||||
(def remove-group
|
||||
(ptk/reify ::remove-group
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [selected (get-in state [:workspace-local :selected])
|
||||
(let [page-id (::page-id state)
|
||||
objects (get-in state [:workspace-data page-id :objects])
|
||||
selected (get-in state [:workspace-local :selected])
|
||||
group-id (first selected)
|
||||
group (get-in state [:workspace-data (::page-id state) :objects group-id])]
|
||||
(if (and (= (count selected) 1) (= (:type group) :group))
|
||||
(let [objects (get-in state [:workspace-data (::page-id state) :objects])
|
||||
shapes (get-in objects [group-id :shapes])
|
||||
group (get objects group-id)]
|
||||
(when (and (= 1 (count selected))
|
||||
(= (:type group) :group))
|
||||
(let [shapes (:shapes group)
|
||||
parent-id (helpers/get-parent group-id objects)
|
||||
parent (get objects parent-id)
|
||||
index-in-parent (->> (:shapes parent)
|
||||
(map-indexed vector)
|
||||
(filter #(#{group-id} (second %)))
|
||||
first first)]
|
||||
(let [rchanges [{:type :mov-objects
|
||||
(ffirst))
|
||||
rchanges [{:type :mov-objects
|
||||
:parent-id parent-id
|
||||
:shapes shapes
|
||||
:index index-in-parent}]
|
||||
|
@ -2400,8 +2278,7 @@
|
|||
:parent-id parent-id
|
||||
:shapes [group-id]
|
||||
:index index-in-parent}]]
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))
|
||||
rx/empty)))))
|
||||
(rx/of (commit-changes rchanges uchanges {:commit-local? true}))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Shortcuts
|
||||
|
@ -2415,8 +2292,8 @@
|
|||
"ctrl+shift+l" #(st/emit! (toggle-layout-flag :layers))
|
||||
"+" #(st/emit! increase-zoom)
|
||||
"-" #(st/emit! decrease-zoom)
|
||||
"ctrl+g" #(st/emit! (create-group))
|
||||
"ctrl+shift+g" #(st/emit! (remove-group))
|
||||
"ctrl+g" #(st/emit! create-group)
|
||||
"ctrl+shift+g" #(st/emit! remove-group)
|
||||
"shift+0" #(st/emit! zoom-to-50)
|
||||
"shift+1" #(st/emit! reset-zoom)
|
||||
"shift+2" #(st/emit! zoom-to-200)
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
[uxbox.main.store :as st]
|
||||
[uxbox.main.ui.dashboard :refer [dashboard]]
|
||||
[uxbox.main.ui.login :refer [login-page]]
|
||||
[uxbox.main.ui.not-found :refer [not-found-page]]
|
||||
[uxbox.main.ui.static :refer [not-found-page not-authorized-page]]
|
||||
[uxbox.main.ui.profile.recovery :refer [profile-recovery-page]]
|
||||
[uxbox.main.ui.profile.recovery-request :refer [profile-recovery-request-page]]
|
||||
[uxbox.main.ui.profile.register :refer [profile-register-page]]
|
||||
|
@ -47,6 +47,7 @@
|
|||
|
||||
["/view/:page-id" :viewer]
|
||||
["/not-found" :not-found]
|
||||
["/not-authorized" :not-authorized]
|
||||
|
||||
(when *assert*
|
||||
["/debug/icons-preview" :debug-icons-preview])
|
||||
|
@ -132,8 +133,11 @@
|
|||
:page-id page-id
|
||||
:key file-id}])
|
||||
|
||||
:not-authorized
|
||||
[:& not-authorized-page]
|
||||
|
||||
:not-found
|
||||
[:& not-found-page {}]))
|
||||
[:& not-found-page]))
|
||||
|
||||
(mf/defc app
|
||||
[]
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
;;
|
||||
;; Copyright (c) 2020 UXBOX Labs SL
|
||||
|
||||
(ns uxbox.main.ui.not-found
|
||||
(ns uxbox.main.ui.static
|
||||
(:require
|
||||
[cljs.spec.alpha :as s]
|
||||
[rumext.alpha :as mf]
|
||||
|
@ -15,7 +15,6 @@
|
|||
|
||||
(mf/defc not-found-page
|
||||
[{:keys [error] :as props}]
|
||||
(js/console.log "not-found" error)
|
||||
[:section.not-found-layout
|
||||
[:div.not-found-header i/logo]
|
||||
[:div.not-found-content
|
||||
|
@ -25,3 +24,14 @@
|
|||
[:div.desc-message "Oops! Page not found"]
|
||||
[:a.btn-primary.btn-small "Go back"]]]])
|
||||
|
||||
(mf/defc not-authorized-page
|
||||
[{:keys [error] :as props}]
|
||||
[:section.not-found-layout
|
||||
[:div.not-found-header i/logo]
|
||||
[:div.not-found-content
|
||||
[:div.message-container
|
||||
[:div.error-img i/icon-lock]
|
||||
[:div.main-message "403"]
|
||||
[:div.desc-message "Sorry, you are not authorized to access this page."]
|
||||
#_[:a.btn-primary.btn-small "Go back"]]]])
|
||||
|
|
@ -32,30 +32,14 @@
|
|||
[uxbox.main.ui.workspace.left-toolbar :refer [left-toolbar]]
|
||||
[uxbox.util.data :refer [classnames]]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.geom.point :as gpt]
|
||||
[uxbox.util.rdnd :as rdnd]))
|
||||
[uxbox.util.geom.point :as gpt]))
|
||||
|
||||
;; --- Workspace
|
||||
|
||||
;; (defn- on-scroll
|
||||
;; [event]
|
||||
;; (let [target (.-target event)
|
||||
;; top (.-scrollTop target)
|
||||
;; left (.-scrollLeft target)]
|
||||
;; (st/emit! (ms/->ScrollEvent (gpt/point left top)))))
|
||||
|
||||
;; (defn- on-wheel
|
||||
;; [event frame]
|
||||
;; (when (kbd/ctrl? event)
|
||||
;; (let [dom (mf/ref-val frame)
|
||||
;; scroll-position (scroll/get-current-position-absolute dom)
|
||||
;; mouse-point @ms/mouse-position]
|
||||
;; (scroll/scroll-to-point dom mouse-point scroll-position))))
|
||||
|
||||
(mf/defc workspace-content
|
||||
[{:keys [page file layout] :as params}]
|
||||
(let [frame (mf/use-ref nil)
|
||||
left-sidebar? (not (empty? (keep layout [:layers :sitemap :document-history :libraries])))
|
||||
(let [left-sidebar? (not (empty? (keep layout [:layers :sitemap
|
||||
:document-history :libraries])))
|
||||
right-sidebar? (not (empty? (keep layout [:icons :drawtools :element-options])))
|
||||
classes (classnames
|
||||
:no-tool-bar-right (not right-sidebar?)
|
||||
|
@ -67,12 +51,7 @@
|
|||
[:& messages]
|
||||
[:& context-menu]
|
||||
|
||||
[:section.workspace-content
|
||||
{:class classes
|
||||
;; :on-scroll on-scroll
|
||||
;; :on-wheel #(on-wheel % frame)
|
||||
}
|
||||
|
||||
[:section.workspace-content {:class classes}
|
||||
[:& history-dialog]
|
||||
|
||||
;; Rules
|
||||
|
@ -81,12 +60,10 @@
|
|||
[:& horizontal-rule]
|
||||
[:& vertical-rule]])
|
||||
|
||||
[:section.workspace-viewport {:id "workspace-viewport"
|
||||
:ref frame}
|
||||
[:section.workspace-viewport {:id "workspace-viewport"}
|
||||
[:& viewport {:page page :file file}]]]
|
||||
|
||||
[:& left-toolbar {:page page
|
||||
:layout layout}]
|
||||
[:& left-toolbar {:page page :layout layout}]
|
||||
|
||||
;; Aside
|
||||
(when left-sidebar?
|
||||
|
@ -94,21 +71,20 @@
|
|||
(when right-sidebar?
|
||||
[:& right-sidebar {:page page :layout layout}])]))
|
||||
|
||||
|
||||
(mf/defc workspace
|
||||
[{:keys [project-id file-id page-id] :as props}]
|
||||
|
||||
(-> (mf/deps file-id page-id)
|
||||
(mf/use-effect
|
||||
(mf/deps file-id page-id)
|
||||
(fn []
|
||||
(st/emit! (dw/initialize project-id file-id page-id))
|
||||
#(st/emit! (dw/finalize project-id file-id page-id)))))
|
||||
#(st/emit! (dw/finalize project-id file-id page-id))))
|
||||
|
||||
(-> (mf/deps file-id)
|
||||
(mf/use-effect
|
||||
(mf/deps file-id)
|
||||
(fn []
|
||||
(st/emit! (dw/initialize-ws file-id))
|
||||
#(st/emit! (dw/finalize-ws file-id)))))
|
||||
#(st/emit! (dw/finalize-ws file-id))))
|
||||
|
||||
(hooks/use-shortcuts dw/shortcuts)
|
||||
|
||||
|
|
|
@ -58,8 +58,8 @@
|
|||
do-hide-shape #(st/emit! (dw/hide-shape (:id shape)))
|
||||
do-lock-shape #(st/emit! (dw/block-shape (:id shape)))
|
||||
do-unlock-shape #(st/emit! (dw/unblock-shape (:id shape)))
|
||||
do-create-group #(st/emit! (dw/create-group))
|
||||
do-remove-group #(st/emit! (dw/remove-group))]
|
||||
do-create-group #(st/emit! dw/create-group)
|
||||
do-remove-group #(st/emit! dw/remove-group)]
|
||||
[:*
|
||||
[:& menu-entry {:title "Copy"
|
||||
:shortcut "Ctrl + c"
|
||||
|
|
|
@ -275,10 +275,7 @@
|
|||
shape)
|
||||
shape (dissoc shape ::initialized? :resize-modifier)]
|
||||
;; Add & select the created shape to the workspace
|
||||
(rx/of dw/deselect-all
|
||||
(if (= :frame (:type shape))
|
||||
(dw/add-frame shape)
|
||||
(dw/add-shape shape))))))))))
|
||||
(rx/of dw/deselect-all (dw/add-shape shape)))))))))
|
||||
|
||||
(def close-drawing-path
|
||||
(ptk/reify ::close-drawing-path
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue