diff --git a/backend/tests/uxbox/tests/test_common_pages.clj b/backend/tests/uxbox/tests/test_common_pages.clj index 8866a6ab9..9f8213737 100644 --- a/backend/tests/uxbox/tests/test_common_pages.clj +++ b/backend/tests/uxbox/tests/test_common_pages.clj @@ -54,14 +54,25 @@ (t/is (= [id-b id-c id-a] (get-in res [:objects uuid/zero :shapes]))))))) (t/deftest process-change-mod-obj - (let [data cp/default-page-data - chg {:type :mod-obj - :id uuid/zero - :operations [{:type :set - :attr :name - :val "foobar"}]} - res (cp/process-changes data [chg])] - (t/is (= "foobar" (get-in res [:objects uuid/zero :name]))))) + (t/testing "simple mod-obj" + (let [data cp/default-page-data + chg {:type :mod-obj + :id uuid/zero + :operations [{:type :set + :attr :name + :val "foobar"}]} + 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 @@ -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 diff --git a/common/uxbox/common/pages.cljc b/common/uxbox/common/pages.cljc index 63537b292..c2435a4e8 100644 --- a/common/uxbox/common/pages.cljc +++ b/common/uxbox/common/pages.cljc @@ -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") - (let [obj (assoc 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] - (fn [shapes] - (cond - (some #{id} shapes) shapes - (nil? index) (conj shapes id) - :else (insert-at-index shapes index [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) (conj shapes 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}] @@ -237,7 +232,7 @@ (contains? (:objects data) frame-id) (update-in [:objects frame-id :shapes] (fn [s] (filterv #(not= % id) s))) - (seq shapes) ; Recursive delete all dependend objects + (seq shapes) ; Recursive delete all dependend objects (as-> $ (reduce #(or (process-change %1 {:type :del-obj :id %2}) %1) $ shapes)))))) (defn- calculate-child-parent-map @@ -275,7 +270,7 @@ (let [invalid (calculate-invalid-targets shape-id (:objects data))] (not (invalid parent-id)))) - valid? (every? is-valid-move shapes) + valid? (every? is-valid-move shapes) ;; Add items into the :shapes property of the target parent-id insert-items @@ -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] diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 2c417a336..2e965dba3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -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" diff --git a/frontend/package.json b/frontend/package.json index 9ee03262d..57757deb4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -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" diff --git a/frontend/src/uxbox/main/data/helpers.cljs b/frontend/src/uxbox/main/data/helpers.cljs index b0ab21386..8fc13ef4d 100644 --- a/frontend/src/uxbox/main/data/helpers.cljs +++ b/frontend/src/uxbox/main/data/helpers.cljs @@ -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 @@ -25,11 +24,11 @@ (let [contains-shape-fn (fn [{:keys [shapes]}] ((set shapes) shape-id)) - shapes (remove #(= (:type %) :frame) (vals objects))] + shapes (remove #(= (:type %) :frame) (vals objects))] (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 diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index ea1456909..9cf9855ce 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -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 - :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/reify ::add-shape 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) + id (uuid/next) + shape (geom/setup-proportions attrs) - 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)) + unames (retrieve-used-names objects) + name (generate-unique-name unames (:name shape)) - uchanges (mapv (fn [rch] - {:type :del-obj - :id (:id rch)}) - rchanges) + frame-id (if (= :frame (:type shape)) + uuid/zero + (calculate-frame-overlap objects shape)) - shapes (mapv :id rchanges) + shape (merge + (if (= :frame (:type shape)) + cp/default-frame-attrs + cp/default-shape-attrs) + (assoc shape + :id id + :name name + :frame-id frame-id)) - rchange {:type :add-obj - :id frame-id - :frame-id uuid/zero - :obj (assoc frame - :id frame-id - :shapes shapes) + rchange {:type :add-obj + :id id + :frame-id frame-id + :obj shape} + uchange {:type :del-obj + :id id}] - :session-id (:session-id state)} + (rx/of (commit-changes [rchange] [uchange] {:commit-local? true}) + (select-shapes #{id})))))) - 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})))))) +;; --- 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 moved-frame}] + + (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,29 +1312,29 @@ (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)] - {:type :add-obj - :id id - :frame-id (:id frm) - :index idx - :obj obj})) - (reverse ids))] - (rx/of (commit-changes rchanges uchanges {:commit-local? true})))))) + cpindex (helpers/calculate-child-parent-map objects) -(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)))))) + 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 (:frame-id obj) + :parent-id (get cpindex id) + :obj obj})) + (reverse (map :id rchanges)))] + (rx/of (commit-changes rchanges uchanges {:commit-local? true})))))) (def delete-selected "Deselect all and remove all selected shapes." @@ -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- 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 [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-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)))))) + (defn- rehash-shape-frame-relationship [ids] - (letfn [(impl-diff [state] - (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) - 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)})) - (recur (first ids) - (rest ids) - rch - uch))))))] - (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})))))))) + (ptk/reify ::rehash-shape-frame-relationship + ptk/WatchEvent + (watch [_ state stream] + (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,73 +2210,75 @@ :width (:width selection-rect) :height (:height selection-rect)}) -(defn create-group [] - (let [id (uuid/next)] - (ptk/reify ::create-group - ptk/WatchEvent - (watch [_ state stream] - (let [selected (get-in state [:workspace-local :selected])] - (if (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) - selection-rect (geom/selection-rect selected-objects) - 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)] +(def create-group + (ptk/reify ::create-group + ptk/WatchEvent + (watch [_ state stream] + (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) + selection-rect (geom/selection-rect selected-objects) + 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 %))) + (ffirst)) - (let [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)}] - uchanges [{:type :mov-objects - :parent-id frame-id - :shapes (into [] 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)))))) + rchanges [{:type :add-obj + :id id + :frame-id frame-id + :obj group-shape + :index index-frame} + {:type :mov-objects + :parent-id id + :shapes (vec selected)}] + uchanges [{:type :mov-objects + :parent-id frame-id + :shapes (vec selected)} + {:type :del-obj + :id id}]] + (rx/of (commit-changes rchanges uchanges {:commit-local? true}) + (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) + parent (get objects parent-id) index-in-parent (->> (:shapes parent) (map-indexed vector) (filter #(#{group-id} (second %))) - first first)] - (let [rchanges [{:type :mov-objects - :parent-id parent-id - :shapes shapes - :index index-in-parent}] - uchanges [{:type :add-obj - :id group-id - :frame-id (:frame-id group) - :obj (assoc group :shapes [])} - {:type :mov-objects - :parent-id group-id - :shapes shapes} - {:type :mov-objects - :parent-id parent-id - :shapes [group-id] - :index index-in-parent}]] - (rx/of (commit-changes rchanges uchanges {:commit-local? true})))) - rx/empty))))) + (ffirst)) + rchanges [{:type :mov-objects + :parent-id parent-id + :shapes shapes + :index index-in-parent}] + uchanges [{:type :add-obj + :id group-id + :frame-id (:frame-id group) + :obj (assoc group :shapes [])} + {:type :mov-objects + :parent-id group-id + :shapes shapes} + {:type :mov-objects + :parent-id parent-id + :shapes [group-id] + :index index-in-parent}]] + (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) diff --git a/frontend/src/uxbox/main/ui.cljs b/frontend/src/uxbox/main/ui.cljs index fc0790045..76aac1b0c 100644 --- a/frontend/src/uxbox/main/ui.cljs +++ b/frontend/src/uxbox/main/ui.cljs @@ -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 [] diff --git a/frontend/src/uxbox/main/ui/not_found.cljs b/frontend/src/uxbox/main/ui/static.cljs similarity index 66% rename from frontend/src/uxbox/main/ui/not_found.cljs rename to frontend/src/uxbox/main/ui/static.cljs index ccd18fedd..93d1089b2 100644 --- a/frontend/src/uxbox/main/ui/not_found.cljs +++ b/frontend/src/uxbox/main/ui/static.cljs @@ -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"]]]]) + diff --git a/frontend/src/uxbox/main/ui/workspace.cljs b/frontend/src/uxbox/main/ui/workspace.cljs index 446937404..6b4dffa58 100644 --- a/frontend/src/uxbox/main/ui/workspace.cljs +++ b/frontend/src/uxbox/main/ui/workspace.cljs @@ -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 - (fn [] - (st/emit! (dw/initialize project-id file-id page-id)) - #(st/emit! (dw/finalize project-id 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)))) - (-> (mf/deps file-id) - (mf/use-effect - (fn [] - (st/emit! (dw/initialize-ws file-id)) - #(st/emit! (dw/finalize-ws file-id))))) + (mf/use-effect + (mf/deps file-id) + (fn [] + (st/emit! (dw/initialize-ws file-id)) + #(st/emit! (dw/finalize-ws file-id)))) (hooks/use-shortcuts dw/shortcuts) diff --git a/frontend/src/uxbox/main/ui/workspace/context_menu.cljs b/frontend/src/uxbox/main/ui/workspace/context_menu.cljs index a0671160c..0e73698ad 100644 --- a/frontend/src/uxbox/main/ui/workspace/context_menu.cljs +++ b/frontend/src/uxbox/main/ui/workspace/context_menu.cljs @@ -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" diff --git a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs index 81243043f..300c39ed7 100644 --- a/frontend/src/uxbox/main/ui/workspace/drawarea.cljs +++ b/frontend/src/uxbox/main/ui/workspace/drawarea.cljs @@ -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