From 8e0c6da1d60267b32856d27a8fd9190592aa29c7 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 11 Jul 2023 08:05:56 +0200 Subject: [PATCH 01/23] :bug: Fix library backup assets order --- common/src/app/common/types/file.cljc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/src/app/common/types/file.cljc b/common/src/app/common/types/file.cljc index 5c3a5d12a..6266a8236 100644 --- a/common/src/app/common/types/file.cljc +++ b/common/src/app/common/types/file.cljc @@ -291,7 +291,7 @@ been modified after the given date." [file-data library since-date] (letfn [(used-assets-shape [shape] - (concat + (concat (ctkl/used-components-changed-since shape library since-date) (ctcl/used-colors-changed-since shape library since-date) (ctyl/used-typographies-changed-since shape library since-date))) @@ -299,7 +299,7 @@ (used-assets-container [container] (->> (mapcat used-assets-shape (ctn/shapes-seq container)) (map #(cons (:id container) %))))] - + (mapcat used-assets-container (containers-seq file-data)))) (defn get-or-add-library-page @@ -407,7 +407,7 @@ (update page :objects update-vals root-to-board))] (-> file-data - (add-instance-grid (sort-by :name components)) + (add-instance-grid (reverse (sort-by :name components))) (update :pages-index update-vals roots-to-board) (assoc-in [:options :components-v2] true)))))))) From 23c8043f34e3069538dadae79d2b7524b4c43348 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 10 Jul 2023 16:56:34 +0200 Subject: [PATCH 02/23] :bug: Fix incorrect message on sending invitation to a member --- CHANGES.md | 1 + backend/src/app/rpc/commands/teams.clj | 37 ++++++++++---------- backend/test/backend_tests/rpc_team_test.clj | 28 +++++++-------- frontend/src/app/main/ui/dashboard/team.cljs | 35 +++++++++--------- 4 files changed, 52 insertions(+), 49 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8db3d6660..3cf9e5bf4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -57,6 +57,7 @@ - Fix focus handling on comments edition [Taiga #5560](https://tree.taiga.io/project/penpot/issue/5560) - Fix incorrect fullname use on registring user after OIDC authentication [Taiga #5517](https://tree.taiga.io/project/penpot/issue/5517) - Fix incorrect modified-at on project after import file [Taiga #5268](https://tree.taiga.io/project/penpot/issue/5268) +- Fix incorrect message after sending invitation to already member [Taiga 5599](https://tree.taiga.io/project/penpot/issue/5599) ### :arrow_up: Deps updates diff --git a/backend/src/app/rpc/commands/teams.clj b/backend/src/app/rpc/commands/teams.clj index 6fa32a2a2..e846f0b34 100644 --- a/backend/src/app/rpc/commands/teams.clj +++ b/backend/src/app/rpc/commands/teams.clj @@ -10,6 +10,7 @@ [app.common.data.macros :as dm] [app.common.exceptions :as ex] [app.common.logging :as l] + [app.common.schema :as sm] [app.common.spec :as us] [app.common.uuid :as uuid] [app.config :as cf] @@ -719,29 +720,22 @@ itoken)))) -(s/def ::email ::us/email) -(s/def ::emails ::us/set-of-valid-emails) -(s/def ::create-team-invitations - (s/keys :req [::rpc/profile-id] - :req-un [::team-id ::role] - :opt-un [::email ::emails])) +(def ^:private schema:create-team-invitations + [:map {:title "create-team-invitations"} + [:team-id ::sm/uuid] + [:role [::sm/one-of #{:owner :admin :editor}]] + [:emails ::sm/set-of-emails]]) (sv/defmethod ::create-team-invitations "A rpc call that allow to send a single or multiple invitations to join the team." - {::doc/added "1.17"} - [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id email emails role] :as params}] + {::doc/added "1.17" + ::sm/params schema:create-team-invitations} + [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id team-id emails role] :as params}] (db/with-atomic [conn pool] (let [perms (get-permissions conn profile-id team-id) profile (db/get-by-id conn :profile profile-id) - team (db/get-by-id conn :team team-id) - - ;; Members emails. We don't re-send inviation to already existing members - member? (into #{} - (map :email) - (db/exec! conn [sql:team-members team-id])) - - emails (cond-> (or emails #{}) (string? email) (conj email))] + team (db/get-by-id conn :team team-id)] (run! (partial quotes/check-quote! conn) (list {::quotes/id ::quotes/invitations-per-team @@ -764,9 +758,13 @@ :hint "looks like the profile has reported repeatedly as spam or has permanent bounces")) (let [cfg (assoc cfg ::db/conn conn) - invitations (into [] + members (->> (db/exec! conn [sql:team-members team-id]) + (into #{} (map :email))) + + invitations (into #{} (comp - (remove member?) + ;; We don't re-send inviation to already existing members + (remove (partial contains? members)) (map (fn [email] {:email (str/lower email) :team team @@ -774,7 +772,8 @@ :role role})) (keep (partial create-invitation cfg))) emails)] - (with-meta invitations + (with-meta {:total (count invitations) + :invitations invitations} {::audit/props {:invitations (count invitations)}}))))) diff --git a/backend/test/backend_tests/rpc_team_test.clj b/backend/test/backend_tests/rpc_team_test.clj index d6c78432c..1685837ae 100644 --- a/backend/test/backend_tests/rpc_team_test.clj +++ b/backend/test/backend_tests/rpc_team_test.clj @@ -37,7 +37,7 @@ :role :editor}] ;; invite external user without complaints - (let [data (assoc data :email "foo@bar.com") + (let [data (assoc data :emails ["foo@bar.com"]) out (th/command! data) ;; retrieve the value from the database and check its content invitation (db/exec-one! @@ -52,7 +52,7 @@ ;; invite internal user without complaints (th/reset-mock! mock) - (let [data (assoc data :email (:email profile2)) + (let [data (assoc data :emails [(:email profile2)]) out (th/command! data)] (t/is (th/success? out)) (t/is (= 1 (:call-count (deref mock))))) @@ -60,7 +60,7 @@ ;; invite user with complaint (th/create-global-complaint-for pool {:type :complaint :email "foo@bar.com"}) (th/reset-mock! mock) - (let [data (assoc data :email "foo@bar.com") + (let [data (assoc data :emails ["foo@bar.com"]) out (th/command! data)] (t/is (th/success? out)) (t/is (= 1 (:call-count (deref mock))))) @@ -79,7 +79,7 @@ (th/reset-mock! mock) (th/create-global-complaint-for pool {:type :bounce :email "foo@bar.com"}) - (let [data (assoc data :email "foo@bar.com") + (let [data (assoc data :emails ["foo@bar.com"]) out (th/command! data)] (t/is (not (th/success? out))) @@ -92,7 +92,7 @@ ;; invite internal user that is muted (th/reset-mock! mock) - (let [data (assoc data :email (:email profile3)) + (let [data (assoc data :emails [(:email profile3)]) out (th/command! data)] (t/is (not (th/success? out))) @@ -118,7 +118,7 @@ ;; Try to invite a not existing user (let [data {::th/type :create-team-invitations ::rpc/profile-id (:id profile1) - :email "notexisting@example.com" + :emails ["notexisting@example.com"] :team-id (:id team) :role :editor} out (th/command! data)] @@ -126,15 +126,15 @@ ;; (th/print-result! out) (t/is (th/success? out)) (t/is (= 1 (:call-count @mock))) - (t/is (= 1 (-> out :result count))) + (t/is (= 1 (-> out :result :total))) - (let [token (-> out :result first) + (let [token (-> out :result :invitations first) claims (tokens/decode sprops token)] (t/is (= :team-invitation (:iss claims))) (t/is (= (:id profile1) (:profile-id claims))) (t/is (= :editor (:role claims))) (t/is (= (:id team) (:team-id claims))) - (t/is (= (:email data) (:member-email claims))) + (t/is (= (first (:emails data)) (:member-email claims))) (t/is (nil? (:member-id claims))))) (th/reset-mock! mock) @@ -142,7 +142,7 @@ ;; Try to invite existing user (let [data {::th/type :create-team-invitations ::rpc/profile-id (:id profile1) - :email (:email profile2) + :emails [(:email profile2)] :team-id (:id team) :role :editor} out (th/command! data)] @@ -150,15 +150,15 @@ ;; (th/print-result! out) (t/is (th/success? out)) (t/is (= 1 (:call-count @mock))) - (t/is (= 1 (-> out :result count))) + (t/is (= 1 (-> out :result :total))) - (let [token (-> out :result first) + (let [token (-> out :result :invitations first) claims (tokens/decode sprops token)] (t/is (= :team-invitation (:iss claims))) (t/is (= (:id profile1) (:profile-id claims))) (t/is (= :editor (:role claims))) (t/is (= (:id team) (:team-id claims))) - (t/is (= (:email data) (:member-email claims))) + (t/is (= (first (:emails data)) (:member-email claims))) (t/is (= (:id profile2) (:member-id claims))))) ))) @@ -264,7 +264,7 @@ ;; invite internal user without complaints (with-redefs [app.config/flags #{}] (th/reset-mock! mock) - (let [data (assoc data :email (:email profile2)) + (let [data (assoc data :emails [(:email profile2)]) out (th/command! data)] (t/is (th/success? out)) (t/is (= 0 (:call-count (deref mock))))) diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index f2a6625c4..e394dcbc6 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -31,8 +31,9 @@ [rumext.v2 :as mf])) (mf/defc header - {::mf/wrap [mf/memo]} - [{:keys [section team] :as props}] + {::mf/wrap [mf/memo] + ::mf/wrap-props false} + [{:keys [section team]}] (let [go-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) go-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) go-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) @@ -98,27 +99,29 @@ (mf/defc invite-members-modal {::mf/register modal/components - ::mf/register-as :invite-members} + ::mf/register-as :invite-members + ::mf/wrap-props false} [{:keys [team origin]}] (let [members-map (mf/deref refs/dashboard-team-members) + perms (:permissions team) - perms (:permissions team) - - roles (mf/use-memo (mf/deps perms) #(get-available-roles perms)) - initial (mf/use-memo (constantly {:role "editor" :team-id (:id team)})) - form (fm/use-form :spec ::invite-member-form - :initial initial) - error-text (mf/use-state "") - - on-success - (fn [] - (st/emit! (msg/success (tr "notifications.invitation-email-sent")) - (modal/hide) - (dd/fetch-team-invitations))) + roles (mf/use-memo (mf/deps perms) #(get-available-roles perms)) + initial (mf/use-memo (constantly {:role "editor" :team-id (:id team)})) + form (fm/use-form :spec ::invite-member-form + :initial initial) + error-text (mf/use-state "") current-data-emails (into #{} (dm/get-in @form [:clean-data :emails])) current-members-emails (into #{} (map (comp :email second)) members-map) + on-success + (fn [form {:keys [total]}] + (when (pos? total) + (st/emit! (msg/success (tr "notifications.invitation-email-sent")))) + + (st/emit! (modal/hide) + (dd/fetch-team-invitations))) + on-error (fn [{:keys [type code] :as error}] (cond From f30ba5876e3c62aab94944c45908bb4873f16ab2 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 10 Jul 2023 17:54:56 +0200 Subject: [PATCH 03/23] :sparkles: Add performance oriented changes to dashboard teams section --- frontend/src/app/main/ui/dashboard/team.cljs | 494 +++++++++++-------- 1 file changed, 280 insertions(+), 214 deletions(-) diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index e394dcbc6..43f16aac7 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -34,21 +34,24 @@ {::mf/wrap [mf/memo] ::mf/wrap-props false} [{:keys [section team]}] - (let [go-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) - go-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) - go-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) - go-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks))) - invite-member (mf/use-fn - (mf/deps team) - #(st/emit! (modal/show {:type :invite-members - :team team - :origin :team}))) + (let [on-nav-members (mf/use-fn #(st/emit! (dd/go-to-team-members))) + on-nav-settings (mf/use-fn #(st/emit! (dd/go-to-team-settings))) + on-nav-invitations (mf/use-fn #(st/emit! (dd/go-to-team-invitations))) + on-nav-webhooks (mf/use-fn #(st/emit! (dd/go-to-team-webhooks))) members-section? (= section :dashboard-team-members) settings-section? (= section :dashboard-team-settings) invitations-section? (= section :dashboard-team-invitations) webhooks-section? (= section :dashboard-team-webhooks) - permissions (:permissions team)] + permissions (:permissions team) + + on-invite-member + (mf/use-fn + (mf/deps team) + (fn [] + (st/emit! (modal/show {:type :invite-members + :team team + :origin :team}))))] [:header.dashboard-header.team [:div.dashboard-title @@ -61,17 +64,19 @@ [:nav.dashboard-header-menu [:ul.dashboard-header-options [:li {:class (when members-section? "active")} - [:a {:on-click go-members} (tr "labels.members")]] + [:a {:on-click on-nav-members} (tr "labels.members")]] [:li {:class (when invitations-section? "active")} - [:a {:on-click go-invitations} (tr "labels.invitations")]] + [:a {:on-click on-nav-invitations} (tr "labels.invitations")]] (when (contains? cfg/flags :webhooks) [:li {:class (when webhooks-section? "active")} - [:a {:on-click go-webhooks} (tr "labels.webhooks")]]) + [:a {:on-click on-nav-webhooks} (tr "labels.webhooks")]]) [:li {:class (when settings-section? "active")} - [:a {:on-click go-settings} (tr "labels.settings")]]]] + [:a {:on-click on-nav-settings} (tr "labels.settings")]]]] [:div.dashboard-buttons (if (and (or invitations-section? members-section?) (:is-admin permissions)) - [:a.btn-secondary.btn-small {:on-click invite-member :data-test "invite-member"} + [:a.btn-secondary.btn-small + {:on-click on-invite-member + :data-test "invite-member"} (tr "dashboard.invite-profile")] [:div.blank-space])]])) @@ -115,7 +120,7 @@ current-members-emails (into #{} (map (comp :email second)) members-map) on-success - (fn [form {:keys [total]}] + (fn [_form {:keys [total]}] (when (pos? total) (st/emit! (msg/success (tr "notifications.invitation-email-sent")))) @@ -188,7 +193,9 @@ ;; MEMBERS SECTION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -(mf/defc member-info [{:keys [member profile] :as props}] +(mf/defc member-info + {::mf/wrap-props false} + [{:keys [member profile]}] (let [is-you? (= (:id profile) (:id member))] [:* [:div.member-image @@ -199,93 +206,97 @@ [:span.you (tr "labels.you")])] [:div.member-email (:email member)]]])) -(mf/defc rol-info [{:keys [member team set-admin set-editor set-owner profile] :as props}] +(mf/defc rol-info + {::mf/wrap-props false} + [{:keys [member team on-set-admin on-set-editor on-set-owner profile]}] (let [member-is-owner? (:is-owner member) member-is-admin? (and (:is-admin member) (not member-is-owner?)) member-is-editor? (and (:can-edit member) (and (not member-is-admin?) (not member-is-owner?))) show? (mf/use-state false) - you-owner? (get-in team [:permissions :is-owner]) - you-admin? (get-in team [:permissions :is-admin]) + + you-owner? (dm/get-in team [:permissions :is-owner]) + you-admin? (dm/get-in team [:permissions :is-admin]) + is-you? (= (:id profile) (:id member)) + can-change-rol? (or you-owner? you-admin?) not-superior? (or you-owner? (and can-change-rol? (or member-is-admin? member-is-editor?))) + role (cond - member-is-owner? "labels.owner" - member-is-admin? "labels.admin" + member-is-owner? "labels.owner" + member-is-admin? "labels.admin" member-is-editor? "labels.editor" - :else "labels.viewer") - is-you? (= (:id profile) (:id member))] + :else "labels.viewer") + + on-show (mf/use-fn #(reset! show? true)) + on-hide (mf/use-fn #(reset! show? false))] [:* (if (and can-change-rol? not-superior? (not (and is-you? you-owner?))) - [:div.rol-selector.has-priv {:on-click #(reset! show? true)} + [:div.rol-selector.has-priv {:on-click on-show} [:span.rol-label (tr role)] [:span.icon i/arrow-down]] [:div.rol-selector [:span.rol-label (tr role)]]) - [:& dropdown {:show @show? - :on-close #(reset! show? false)} + [:& dropdown {:show @show? :on-close on-hide} [:ul.dropdown.options-dropdown - [:li {:on-click set-admin} (tr "labels.admin")] - [:li {:on-click set-editor} (tr "labels.editor")] + [:li {:on-click on-set-admin} (tr "labels.admin")] + [:li {:on-click on-set-editor} (tr "labels.editor")] ;; Temporarily disabled viewer role ;; https://tree.taiga.io/project/penpot/issue/1083 ;; [:li {:on-click set-viewer} (tr "labels.viewer")] (when you-owner? - [:li {:on-click (partial set-owner member)} (tr "labels.owner")])]]])) + [:li {:on-click (partial on-set-owner member)} (tr "labels.owner")])]]])) + +(mf/defc member-actions + {::mf/wrap-props false} + [{:keys [member team on-delete on-leave profile]}] + (let [is-owner? (:is-owner member) + owner? (dm/get-in team [:permissions :is-owner]) + admin? (dm/get-in team [:permissions :is-admin]) + show? (mf/use-state false) + is-you? (= (:id profile) (:id member)) + can-delete? (or owner? admin?) + + on-show (mf/use-fn #(reset! show? true)) + on-hide (mf/use-fn #(reset! show? false))] -(mf/defc member-actions [{:keys [member team delete leave profile] :as props}] - (let [is-owner? (:is-owner member) - owner? (get-in team [:permissions :is-owner]) - admin? (get-in team [:permissions :is-admin]) - show? (mf/use-state false) - is-you? (= (:id profile) (:id member)) - can-delete? (or owner? admin?)] [:* (when (or is-you? (and can-delete? (not (and is-owner? (not owner?))))) - [:span.icon {:on-click #(reset! show? true)} [i/actions]]) - [:& dropdown {:show @show? - :on-close #(reset! show? false)} + [:span.icon {:on-click on-show} [i/actions]]) + + [:& dropdown {:show @show? :on-close on-hide} [:ul.dropdown.actions-dropdown (when is-you? - [:li {:on-click leave} (tr "dashboard.leave-team")]) + [:li {:on-click on-leave} (tr "dashboard.leave-team")]) (when (and can-delete? (not is-you?) (not (and is-owner? (not owner?)))) - [:li {:on-click delete} (tr "labels.remove-member")])]]])) + [:li {:on-click on-delete} (tr "labels.remove-member")])]]])) + +(defn- set-role! [member-id role] + (let [params {:member-id member-id :role role}] + (st/emit! (dd/update-team-member-role params)))) (mf/defc team-member - {::mf/wrap [mf/memo]} - [{:keys [team member members profile] :as props}] + {::mf/wrap [mf/memo] + ::mf/wrap-props false} + [{:keys [team member members profile]}] - (let [owner? (dm/get-in team [:permissions :is-owner]) - set-role + (let [member-id (:id member) + on-set-admin (mf/use-fn (mf/deps member-id) (partial set-role! member-id :admin)) + on-set-editor (mf/use-fn (mf/deps member-id) (partial set-role! member-id :editor)) + owner? (dm/get-in team [:permissions :is-owner]) + + on-set-owner (mf/use-fn (mf/deps member) - (fn [role] - (let [params {:member-id (:id member) :role role}] - (st/emit! (dd/update-team-member-role params))))) - - - set-owner-fn (mf/use-fn (mf/deps set-role) (partial set-role :owner)) - set-admin (mf/use-fn (mf/deps set-role) (partial set-role :admin)) - set-editor (mf/use-fn (mf/deps set-role) (partial set-role :editor)) - ;; set-viewer (partial set-role :viewer) - - set-owner - (mf/use-fn - (mf/deps set-owner-fn member) - (fn [member] - (st/emit! (modal/show - {:type :confirm - :title (tr "modals.promote-owner-confirm.title") - :message (tr "modals.promote-owner-confirm.message" (:name member)) - :scd-message (tr "modals.promote-owner-confirm.hint") - :accept-label (tr "modals.promote-owner-confirm.accept") - :on-accept set-owner-fn - :accept-style :primary})))) - - delete-member-fn - (mf/use-fn - (mf/deps member) - (fn [] (st/emit! (dd/delete-team-member {:member-id (:id member)})))) + (fn [member _event] + (let [params {:type :confirm + :title (tr "modals.promote-owner-confirm.title") + :message (tr "modals.promote-owner-confirm.message" (:name member)) + :scd-message (tr "modals.promote-owner-confirm.hint") + :accept-label (tr "modals.promote-owner-confirm.accept") + :on-accept (partial set-role! member-id :owner) + :accept-style :primary}] + (st/emit! (modal/show params))))) on-success (mf/use-fn @@ -311,14 +322,14 @@ (rx/throw error)))) - delete-fn + on-delete-accepted (mf/use-fn (mf/deps team on-success on-error) (fn [] (st/emit! (dd/delete-team (with-meta team {:on-success on-success :on-error on-error}))))) - leave-fn + on-leave-accepted (mf/use-fn (mf/deps on-success on-error) (fn [member-id] @@ -327,9 +338,9 @@ {:on-success on-success :on-error on-error})))))) - leave-and-close + on-leave-and-close (mf/use-fn - (mf/deps delete-fn) + (mf/deps on-delete-accepted) (fn [] (st/emit! (modal/show {:type :confirm @@ -337,80 +348,100 @@ :message (tr "modals.leave-and-close-confirm.message" (:name team)) :scd-message (tr "modals.leave-and-close-confirm.hint") :accept-label (tr "modals.leave-confirm.accept") - :on-accept delete-fn})))) + :on-accept on-delete-accepted})))) - change-owner-and-leave + on-change-owner-and-leave (mf/use-fn - (mf/deps profile team leave-fn) + (mf/deps profile team on-leave-accepted) (fn [] (st/emit! (dd/fetch-team-members) (modal/show {:type :leave-and-reassign :profile profile :team team - :accept leave-fn})))) + :accept on-leave-accepted})))) - leave + on-leave (mf/use-fn - (mf/deps leave-fn) + (mf/deps on-leave-accepted) (fn [] (st/emit! (modal/show {:type :confirm :title (tr "modals.leave-confirm.title") :message (tr "modals.leave-confirm.message") :accept-label (tr "modals.leave-confirm.accept") - :on-accept leave-fn})))) + :on-accept on-leave-accepted})))) - preset-leave (cond (= 1 (count members)) leave-and-close - (= true owner?) change-owner-and-leave - :else leave) - - delete + on-delete (mf/use-fn - (mf/deps delete-member-fn) + (mf/deps member-id) (fn [] - (st/emit! (modal/show - {:type :confirm - :title (tr "modals.delete-team-member-confirm.title") - :message (tr "modals.delete-team-member-confirm.message") - :accept-label (tr "modals.delete-team-member-confirm.accept") - :on-accept delete-member-fn}))))] + (let [on-accept #(st/emit! (dd/delete-team-member {:member-id member-id})) + params {:type :confirm + :title (tr "modals.delete-team-member-confirm.title") + :message (tr "modals.delete-team-member-confirm.message") + :accept-label (tr "modals.delete-team-member-confirm.accept") + :on-accept on-accept}] + (st/emit! (modal/show params))))) + + on-leave' + (cond (= 1 (count members)) on-leave-and-close + (= true owner?) on-change-owner-and-leave + :else on-leave)] [:div.table-row [:div.table-field.name [:& member-info {:member member :profile profile}]] + [:div.table-field.roles [:& rol-info {:member member :team team - :set-admin set-admin - :set-editor set-editor - :set-owner set-owner + :on-set-admin on-set-admin + :on-set-editor on-set-editor + :on-set-owner on-set-owner :profile profile}]] + [:div.table-field.actions [:& member-actions {:member member :profile profile :team team - :delete delete - :leave preset-leave}]]])) + :on-delete on-delete + :on-leave on-leave'}]]])) (mf/defc team-members - [{:keys [members-map team profile] :as props}] - (let [members (->> (vals members-map) - (sort-by :created-at) - (remove :is-owner)) - owner (->> (vals members-map) - (d/seek :is-owner))] + {::mf/wrap-props false} + [{:keys [members-map team profile]}] + (let [members (mf/with-memo [members-map] + (->> (vals members-map) + (sort-by :created-at) + (remove :is-owner))) + owner (mf/with-memo [members-map] + (->> (vals members-map) + (d/seek :is-owner)))] + [:div.dashboard-table.team-members [:div.table-header [:div.table-field.name (tr "labels.member")] [:div.table-field.role (tr "labels.role")]] + [:div.table-rows - [:& team-member {:member owner :team team :profile profile :members members-map}] + [:& team-member + {:member owner + :team team + :profile profile + :members members-map}] + (for [item members] - [:& team-member {:member item :team team :profile profile :key (:id item) :members members-map}])]])) + [:& team-member + {:member item + :team team + :profile profile + :key (:id item) + :members members-map}])]])) (mf/defc team-members-page - [{:keys [team profile] :as props}] + {::mf/wrap-props false} + [{:keys [team profile]}] (let [members-map (mf/deref refs/dashboard-team-members)] (mf/with-effect [team] @@ -420,74 +451,76 @@ (tr "dashboard.your-penpot") (:name team))))) - (mf/with-effect + (mf/with-effect [] (st/emit! (dd/fetch-team-members))) [:* - [:& header {:section :dashboard-team-members - :team team}] + [:& header {:section :dashboard-team-members :team team}] [:section.dashboard-container.dashboard-team-members - [:& team-members {:profile profile - :team team - :members-map members-map}]]])) + [:& team-members + {:profile profile + :team team + :members-map members-map}]]])) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; INVITATIONS SECTION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (mf/defc invitation-role-selector - [{:keys [can-invite? role status change-to-admin change-to-editor] :as props}] - (let [show? (mf/use-state false) - role-label (cond - (= role :owner) "labels.owner" - (= role :admin) "labels.admin" - (= role :editor) "labels.editor" - :else "labels.viewer")] + {::mf/wrap-props false} + [{:keys [can-invite? role status on-change]}] + (let [show? (mf/use-state false) + label (cond + (= role :owner) (tr "labels.owner") + (= role :admin) (tr "labels.admin") + (= role :editor) (tr "labels.editor") + :else (tr "labels.viewer")) + + on-hide (mf/use-fn #(reset! show? false)) + on-show (mf/use-fn #(reset! show? true)) + + on-change' + (mf/use-fn + (mf/deps on-change) + (fn [event] + (let [role (-> (dom/get-current-target event) + (dom/get-data "role") + (keyword))] + (on-change role event))))] + [:* (if (and can-invite? (= status :pending)) - [:div.rol-selector.has-priv {:on-click #(reset! show? true)} - [:span.rol-label (tr role-label)] + [:div.rol-selector.has-priv {:on-click on-show} + [:span.rol-label label] [:span.icon i/arrow-down]] [:div.rol-selector - [:span.rol-label (tr role-label)]]) + [:span.rol-label label]]) - [:& dropdown {:show @show? - :on-close #(reset! show? false)} + [:& dropdown {:show @show? :on-close on-hide} [:ul.dropdown.options-dropdown - [:li {:on-click change-to-admin} (tr "labels.admin")] - [:li {:on-click change-to-editor} (tr "labels.editor")]]]])) + [:li {:data-role "admin" :on-click on-change'} (tr "labels.admin")] + [:li {:data-role "editor" :on-click on-change'} (tr "labels.editor")]]]])) (mf/defc invitation-status-badge - [{:keys [status] :as props}] - (let [status-label (if (= status :expired) - (tr "labels.expired-invitation") - (tr "labels.pending-invitation"))] - [:div.status-badge {:class (dom/classnames - :expired (= status :expired) - :pending (= status :pending))} - [:span.status-label (tr status-label)]])) + {::mf/wrap-props false} + [{:keys [status]}] + [:div.status-badge + {:class (dom/classnames + :expired (= status :expired) + :pending (= status :pending))} + [:span.status-label + (if (= status :expired) + (tr "labels.expired-invitation") + (tr "labels.pending-invitation"))]]) (mf/defc invitation-actions - [{:keys [invitation team] :as props}] + {::mf/wrap-props false} + [{:keys [invitation team-id]}] (let [show? (mf/use-state false) - team-id (:id team) email (:email invitation) role (:role invitation) - on-resend-success - (mf/use-fn - (fn [] - (st/emit! (msg/success (tr "notifications.invitation-email-sent")) - (modal/hide) - (dd/fetch-team-invitations)))) - - on-copy-success - (mf/use-fn - (fn [] - (st/emit! (msg/success (tr "notifications.invitation-link-copied")) - (modal/hide)))) - on-error (mf/use-fn (mf/deps email) @@ -508,7 +541,7 @@ :else (rx/throw error)))) - delete-fn + on-delete (mf/use-fn (mf/deps email team-id) (fn [] @@ -516,7 +549,15 @@ mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}] (st/emit! (dd/delete-team-invitation (with-meta params mdata)))))) - resend-fn + + on-resend-success + (mf/use-fn + (fn [] + (st/emit! (msg/success (tr "notifications.invitation-email-sent")) + (modal/hide) + (dd/fetch-team-invitations)))) + + on-resend (mf/use-fn (mf/deps email team-id) (fn [] @@ -530,7 +571,13 @@ (-> (dd/invite-team-members params) (with-meta {::ev/origin :team})))))) - copy-fn + on-copy-success + (mf/use-fn + (fn [] + (st/emit! (msg/success (tr "notifications.invitation-link-copied")) + (modal/hide)))) + + on-copy (mf/use-fn (mf/deps email team-id) (fn [] @@ -539,52 +586,55 @@ :on-error on-error})] (st/emit! (-> (dd/copy-invitation-link params) - (with-meta {::ev/origin :team}))))))] + (with-meta {::ev/origin :team})))))) + on-hide (mf/use-fn #(reset! show? false)) + on-show (mf/use-fn #(reset! show? true))] [:* - [:span.icon {:on-click #(reset! show? true)} [i/actions]] - [:& dropdown {:show @show? - :on-close #(reset! show? false)} + [:span.icon {:on-click on-show} [i/actions]] + [:& dropdown {:show @show? :on-close on-hide} [:ul.dropdown.actions-dropdown - [:li {:on-click copy-fn} (tr "labels.copy-invitation-link")] - [:li {:on-click resend-fn} (tr "labels.resend-invitation")] - [:li {:on-click delete-fn} (tr "labels.delete-invitation")]]]])) + [:li {:on-click on-copy} (tr "labels.copy-invitation-link")] + [:li {:on-click on-resend} (tr "labels.resend-invitation")] + [:li {:on-click on-delete} (tr "labels.delete-invitation")]]]])) (mf/defc invitation-row - {::mf/wrap [mf/memo]} - [{:keys [invitation can-invite? team] :as props}] + {::mf/wrap [mf/memo] + ::mf/wrap-props false} + [{:keys [invitation can-invite? team-id] :as props}] (let [expired? (:expired invitation) email (:email invitation) role (:role invitation) status (if expired? :expired :pending) - change-rol + on-change-role (mf/use-fn - (mf/deps team email) - (fn [role] - (let [params {:email email :team-id (:id team) :role role} + (mf/deps email team-id) + (fn [role _event] + (let [params {:email email :team-id team-id :role role} mdata {:on-success #(st/emit! (dd/fetch-team-invitations))}] (st/emit! (dd/update-team-invitation-role (with-meta params mdata))))))] [:div.table-row [:div.table-field.mail email] + [:div.table-field.roles [:& invitation-role-selector {:can-invite? can-invite? :role role :status status - :change-to-editor (partial change-rol :editor) - :change-to-admin (partial change-rol :admin)}]] + :on-change on-change-role}]] [:div.table-field.status [:& invitation-status-badge {:status status}]] + [:div.table-field.actions (when can-invite? [:& invitation-actions {:invitation invitation - :team team}])]])) + :team-id team-id}])]])) (mf/defc empty-invitation-table [{:keys [can-invite?] :as props}] @@ -598,7 +648,8 @@ [{:keys [team invitations] :as props}] (let [owner? (dm/get-in team [:permissions :is-owner]) admin? (dm/get-in team [:permissions :is-admin]) - can-invite? (or owner? admin?)] + can-invite? (or owner? admin?) + team-id (:id team)] [:div.dashboard-table.invitations [:div.table-header @@ -613,7 +664,7 @@ {:key (:email invitation) :invitation invitation :can-invite? can-invite? - :team team}])])])) + :team-id team-id}])])])) (mf/defc team-invitations-page [{:keys [team] :as props}] @@ -770,6 +821,7 @@ (mf/defc webhooks-hero + {::mf/wrap-props false} [] [:div.banner [:div.title (tr "labels.webhooks") @@ -788,18 +840,22 @@ [:span (tr "dashboard.webhooks.create")]]]]) (mf/defc webhook-actions - [{:keys [on-edit on-delete] :as props}] - (let [show? (mf/use-state false)] + {::mf/wrap-props false} + [{:keys [on-edit on-delete]}] + (let [show? (mf/use-state false) + on-show (mf/use-fn #(reset! show? true)) + on-hide (mf/use-fn #(reset! show? false))] + [:* - [:span.icon {:on-click #(reset! show? true)} [i/actions]] - [:& dropdown {:show @show? - :on-close #(reset! show? false)} + [:span.icon {:on-click on-show} [i/actions]] + [:& dropdown {:show @show? :on-close on-hide} [:ul.dropdown.actions-dropdown [:li {:on-click on-edit} (tr "labels.edit")] [:li {:on-click on-delete} (tr "labels.delete")]]]])) (mf/defc last-delivery-icon - [{:keys [success? text] :as props}] + {::mf/wrap-props false} + [{:keys [success? text]}] [:div.last-delivery-icon [:div.tooltip [:div.label text] @@ -811,34 +867,44 @@ (mf/defc webhook-item {::mf/wrap [mf/memo]} [{:keys [webhook] :as props}] - (let [on-edit #(st/emit! (modal/show :webhook {:webhook webhook})) - error-code (:error-code webhook) + (let [error-code (:error-code webhook) + id (:id webhook) - delete-fn - (fn [] - (let [params {:id (:id webhook)} - mdata {:on-success #(st/emit! (dd/fetch-team-webhooks))}] - (st/emit! (dd/delete-team-webhook (with-meta params mdata))))) + on-edit + (mf/use-fn + (mf/deps webhook) + (fn [] + (st/emit! (modal/show :webhook {:webhook webhook})))) + + on-delete-accepted + (mf/use-fn + (mf/deps id) + (fn [] + (let [params {:id id} + mdata {:on-success #(st/emit! (dd/fetch-team-webhooks))}] + (st/emit! (dd/delete-team-webhook (with-meta params mdata)))))) on-delete - (fn [] - (st/emit! (modal/show - {:type :confirm - :title (tr "modals.delete-webhook.title") - :message (tr "modals.delete-webhook.message") - :accept-label (tr "modals.delete-webhook.accept") - :on-accept delete-fn}))) + (mf/use-fn + (mf/deps on-delete-accepted) + (fn [] + (let [params {:type :confirm + :title (tr "modals.delete-webhook.title") + :message (tr "modals.delete-webhook.message") + :accept-label (tr "modals.delete-webhook.accept") + :on-accept on-delete-accepted}] + (st/emit! (modal/show params))))) last-delivery-text (if (nil? error-code) (tr "webhooks.last-delivery.success") - (str (tr "errors.webhooks.last-delivery") - (cond - (= error-code "ssl-validation-error") - (dm/str " " (tr "errors.webhooks.ssl-validation")) + (dm/str (tr "errors.webhooks.last-delivery") + (cond + (= error-code "ssl-validation-error") + (dm/str " " (tr "errors.webhooks.ssl-validation")) - (str/starts-with? error-code "unexpected-status") - (dm/str " " (tr "errors.webhooks.unexpected-status" (extract-status error-code))))))] + (str/starts-with? error-code "unexpected-status") + (dm/str " " (tr "errors.webhooks.unexpected-status" (extract-status error-code))))))] [:div.table-row [:div.table-field.last-delivery @@ -858,14 +924,16 @@ :on-delete on-delete}]]])) (mf/defc webhooks-list - [{:keys [webhooks] :as props}] + {::mf/wrap-props false} + [{:keys [webhooks]}] [:div.dashboard-table [:div.table-rows (for [webhook webhooks] [:& webhook-item {:webhook webhook :key (:id webhook)}])]]) (mf/defc team-webhooks-page - [{:keys [team] :as props}] + {::mf/wrap-props false} + [{:keys [team]}] (let [webhooks (mf/deref refs/dashboard-team-webhooks)] (mf/with-effect [team] @@ -894,7 +962,8 @@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (mf/defc team-settings-page - [{:keys [team] :as props}] + {::mf/wrap-props false} + [{:keys [team]}] (let [finput (mf/use-ref) members-map (mf/deref refs/dashboard-team-members) @@ -915,22 +984,19 @@ (st/emit! (dd/update-team-photo file)))] - (mf/use-effect - (mf/deps team) - (fn [] - (dom/set-html-title (tr "title.team-settings" - (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team)))))) + (mf/with-effect [team] + (dom/set-html-title (tr "title.team-settings" + (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))))) - (mf/use-effect - #(st/emit! (dd/fetch-team-members) + (mf/with-effect [] + (st/emit! (dd/fetch-team-members) (dd/fetch-team-stats))) [:* - [:& header {:section :dashboard-team-settings - :team team}] + [:& header {:section :dashboard-team-settings :team team}] [:section.dashboard-container.dashboard-team-settings [:div.team-settings [:div.horizontal-blocks From 41924246aa793a68c42acd397f41d4751f4b89ca Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 11 Jul 2023 08:52:36 +0200 Subject: [PATCH 04/23] :bug: Fix text decoration on button --- CHANGES.md | 1 + frontend/resources/styles/main/partials/inspect.scss | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 3cf9e5bf4..3e6827101 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -58,6 +58,7 @@ - Fix incorrect fullname use on registring user after OIDC authentication [Taiga #5517](https://tree.taiga.io/project/penpot/issue/5517) - Fix incorrect modified-at on project after import file [Taiga #5268](https://tree.taiga.io/project/penpot/issue/5268) - Fix incorrect message after sending invitation to already member [Taiga 5599](https://tree.taiga.io/project/penpot/issue/5599) +- Fix text decoration on button [Taiga #5301](https://tree.taiga.io/project/penpot/issue/5301) ### :arrow_up: Deps updates diff --git a/frontend/resources/styles/main/partials/inspect.scss b/frontend/resources/styles/main/partials/inspect.scss index 32ce5a9e7..043ba2c8d 100644 --- a/frontend/resources/styles/main/partials/inspect.scss +++ b/frontend/resources/styles/main/partials/inspect.scss @@ -290,6 +290,7 @@ border-radius: $br4; margin: 0.5rem; cursor: pointer; + text-decoration: none; &:hover { background-color: $color-primary; From 2e461b30706b773e23f45f50828c64e8bc7671dd Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 11 Jul 2023 08:55:49 +0200 Subject: [PATCH 05/23] :bug: Fix text menu order on design tab --- CHANGES.md | 1 + .../ui/workspace/sidebar/options/shapes/multiple.cljs | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3e6827101..0cc1e8073 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -59,6 +59,7 @@ - Fix incorrect modified-at on project after import file [Taiga #5268](https://tree.taiga.io/project/penpot/issue/5268) - Fix incorrect message after sending invitation to already member [Taiga 5599](https://tree.taiga.io/project/penpot/issue/5599) - Fix text decoration on button [Taiga #5301](https://tree.taiga.io/project/penpot/issue/5301) +- Fix menu order on design tab [Taiga #5195](https://tree.taiga.io/project/penpot/issue/5195) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index bdef18f32..e93eb9314 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -309,12 +309,12 @@ [measure-ids measure-values] (get-attrs shapes objects :measure) [layer-ids layer-values + text-ids text-values constraint-ids constraint-values fill-ids fill-values shadow-ids shadow-values blur-ids blur-values stroke-ids stroke-values - text-ids text-values exports-ids exports-values layout-container-ids layout-container-values layout-item-ids layout-item-values] @@ -325,12 +325,12 @@ [] (mapcat identity) [(get-attrs shapes objects-no-measures :layer) + (get-attrs shapes objects-no-measures :text) (get-attrs shapes objects-no-measures :constraint) (get-attrs shapes objects-no-measures :fill) (get-attrs shapes objects-no-measures :shadow) (get-attrs shapes objects-no-measures :blur) (get-attrs shapes objects-no-measures :stroke) - (get-attrs shapes objects-no-measures :text) (get-attrs shapes objects-no-measures :exports) (get-attrs shapes objects-no-measures :layout-container) (get-attrs shapes objects-no-measures :layout-item) @@ -356,6 +356,9 @@ (when-not (empty? layer-ids) [:& layer-menu {:type type :ids layer-ids :values layer-values}]) + (when-not (empty? text-ids) + [:& ot/text-menu {:type type :ids text-ids :values text-values}]) + (when-not (empty? fill-ids) [:& fill-menu {:type type :ids fill-ids :values fill-values}]) @@ -372,8 +375,5 @@ (when-not (empty? blur-ids) [:& blur-menu {:type type :ids blur-ids :values blur-values}]) - (when-not (empty? text-ids) - [:& ot/text-menu {:type type :ids text-ids :values text-values}]) - (when-not (empty? exports-ids) [:& exports-menu {:type type :ids exports-ids :values exports-values :page-id page-id :file-id file-id}])])) From 3dcd640a998f3a9ab971fb8d1e90990f4dac1e5a Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 11 Jul 2023 09:46:29 +0200 Subject: [PATCH 06/23] :bug: Fix search bar width on layer tab --- CHANGES.md | 1 + .../resources/styles/main/partials/sidebar-layers.scss | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 0cc1e8073..df879824b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -60,6 +60,7 @@ - Fix incorrect message after sending invitation to already member [Taiga 5599](https://tree.taiga.io/project/penpot/issue/5599) - Fix text decoration on button [Taiga #5301](https://tree.taiga.io/project/penpot/issue/5301) - Fix menu order on design tab [Taiga #5195](https://tree.taiga.io/project/penpot/issue/5195) +- Fix search bar width on layer tab [Taiga #5445](https://tree.taiga.io/project/penpot/issue/5445) ### :arrow_up: Deps updates diff --git a/frontend/resources/styles/main/partials/sidebar-layers.scss b/frontend/resources/styles/main/partials/sidebar-layers.scss index e9d3256b5..f408a8fc4 100644 --- a/frontend/resources/styles/main/partials/sidebar-layers.scss +++ b/frontend/resources/styles/main/partials/sidebar-layers.scss @@ -378,6 +378,8 @@ span.element-name { background-color: $color-gray-50; color: $color-white; font-size: $fs12; + flex-grow: 1; + margin: 0; height: 16px; &:focus { outline: none; @@ -386,10 +388,16 @@ span.element-name { div { height: 16px; overflow: hidden; + width: 100%; + display: flex; + align-items: center; } .filter, .clear { width: 35px; + display: flex; + justify-content: center; + align-items: center; &.active { svg { fill: $color-primary; From 52545692dfe76d17723fc6396b445b33d910a9d9 Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 11 Jul 2023 11:26:31 +0200 Subject: [PATCH 07/23] :bug: Fix border radius values with decimals --- CHANGES.md | 3 ++- frontend/src/app/util/code_gen.cljs | 14 +++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index df879824b..4ebb767d4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -61,7 +61,8 @@ - Fix text decoration on button [Taiga #5301](https://tree.taiga.io/project/penpot/issue/5301) - Fix menu order on design tab [Taiga #5195](https://tree.taiga.io/project/penpot/issue/5195) - Fix search bar width on layer tab [Taiga #5445](https://tree.taiga.io/project/penpot/issue/5445) - +- Fix border radius values with decimals [Taiga #5283](https://tree.taiga.io/project/penpot/issue/5283) + ### :arrow_up: Deps updates - Update google fonts catalog (at 2023/07/06) [Taiga #5592](https://tree.taiga.io/project/penpot/issue/5592) diff --git a/frontend/src/app/util/code_gen.cljs b/frontend/src/app/util/code_gen.cljs index f3cf5eea4..f50247b56 100644 --- a/frontend/src/app/util/code_gen.cljs +++ b/frontend/src/app/util/code_gen.cljs @@ -75,6 +75,14 @@ (fmt/format-size :width value values) (fmt/format-size :heigth value values)))) +(defn format-border-radius + [values] + + (and (coll? values) (d/not-empty? values)) + (->> values + (map fmt/format-pixels) + (str/join " "))) + (defn styles-data [shape] {:position {:props [:type] @@ -90,7 +98,7 @@ :rx "border-radius" :r1 "border-radius"} :format {:rotation #(str/fmt "rotate(%sdeg)" %) - :r1 #(apply str/fmt "%spx %spx %spx %spx" %) + :r1 format-border-radius :width #(get-size :width %) :height #(get-size :height %)} :multi {:r1 [:r1 :r2 :r3 :r4]}} @@ -236,9 +244,9 @@ (str/join "\n"))))) (defn shape->properties [shape] - (let [;; This property is added in an earlier step (code.cljs), + (let [;; This property is added in an earlier step (code.cljs), ;; it will come with a vector of flex-items if any. - ;; If there are none it will continue as usual. + ;; If there are none it will continue as usual. flex-items (:flex-items shape) props (->> (styles-data shape) vals (mapcat :props)) to-prop (->> (styles-data shape) vals (map :to-prop) (reduce merge)) From b94885a764485544c4c1d79d230abb594fce54d9 Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 11 Jul 2023 13:30:34 +0200 Subject: [PATCH 08/23] :bug: Fix shortcut translation --- CHANGES.md | 1 + frontend/translations/cs.po | 3 --- frontend/translations/en.po | 18 +++++++++--------- frontend/translations/he.po | 3 --- frontend/translations/hr.po | 3 --- frontend/translations/id.po | 3 --- frontend/translations/lv.po | 3 --- frontend/translations/pl.po | 3 --- frontend/translations/pt_BR.po | 3 --- frontend/translations/pt_PT.po | 3 --- frontend/translations/ro.po | 3 --- frontend/translations/tr.po | 3 --- frontend/translations/zh_CN.po | 3 --- 13 files changed, 10 insertions(+), 42 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4ebb767d4..5d11dac02 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -62,6 +62,7 @@ - Fix menu order on design tab [Taiga #5195](https://tree.taiga.io/project/penpot/issue/5195) - Fix search bar width on layer tab [Taiga #5445](https://tree.taiga.io/project/penpot/issue/5445) - Fix border radius values with decimals [Taiga #5283](https://tree.taiga.io/project/penpot/issue/5283) +- Fix shortcuts translations not homogenized [Taiga #5141](https://tree.taiga.io/project/penpot/issue/5141) ### :arrow_up: Deps updates diff --git a/frontend/translations/cs.po b/frontend/translations/cs.po index 6737bd477..90b8ecd2c 100644 --- a/frontend/translations/cs.po +++ b/frontend/translations/cs.po @@ -2321,9 +2321,6 @@ msgstr "Přichytit k vodicím lištám" msgid "shortcuts.toggle-textpalette" msgstr "Přepnout paletu textu" -msgid "shortcuts.toggle-visibility" -msgstr "Přepnout viditelnost" - msgid "shortcuts.toggle-zoom-style" msgstr "Přepnout styl přiblížení" diff --git a/frontend/translations/en.po b/frontend/translations/en.po index bfefc32b1..7c5e2f162 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -2528,7 +2528,7 @@ msgid "shortcuts.h-distribute" msgstr "Distribute horizontally" msgid "shortcuts.hide-ui" -msgstr "Show/hide UI" +msgstr "Show / Hide UI" msgid "shortcuts.increase-zoom" msgstr "Zoom in" @@ -2687,10 +2687,10 @@ msgid "shortcuts.separate-nodes" msgstr "Separate nodes" msgid "shortcuts.show-pixel-grid" -msgstr "Show/hide pixel grid" +msgstr "Show / Hide pixel grid" msgid "shortcuts.show-shortcuts" -msgstr "Show/hide shortcuts" +msgstr "Show / Hide shortcuts" msgid "shortcuts.snap-nodes" msgstr "Snap to nodes" @@ -2742,7 +2742,7 @@ msgid "shortcuts.toggle-fullscreen" msgstr "Toggle fullscreen" msgid "shortcuts.toggle-grid" -msgstr "Show/hide grid" +msgstr "Show / Hide grid" msgid "shortcuts.toggle-history" msgstr "Toggle history" @@ -2751,16 +2751,16 @@ msgid "shortcuts.toggle-layers" msgstr "Toggle layers" msgid "shortcuts.toggle-layout-flex" -msgstr "Add/remove flex layout" +msgstr "Add / Remove flex layout" msgid "shortcuts.toggle-lock" -msgstr "Lock selected" +msgstr "Lock / Unlock" msgid "shortcuts.toggle-lock-size" msgstr "Lock proportions" msgid "shortcuts.toggle-rules" -msgstr "Show/hide rulers" +msgstr "Show / Hide rulers" msgid "shortcuts.toggle-scale-text" msgstr "Toggle scale text" @@ -2775,7 +2775,7 @@ msgid "shortcuts.toggle-textpalette" msgstr "Toggle text palette" msgid "shortcuts.toggle-visibility" -msgstr "Toggle visibility" +msgstr "Show / Hide" msgid "shortcuts.toggle-zoom-style" msgstr "Toggle zoom style" @@ -4472,7 +4472,7 @@ msgid "workspace.shape.menu.hide" msgstr "Hide" msgid "workspace.shape.menu.hide-ui" -msgstr "Show/Hide UI" +msgstr "Show / Hide UI" msgid "workspace.shape.menu.intersection" msgstr "Intersection" diff --git a/frontend/translations/he.po b/frontend/translations/he.po index e58d6cfd3..caa8c85df 100644 --- a/frontend/translations/he.po +++ b/frontend/translations/he.po @@ -2592,9 +2592,6 @@ msgstr "הצמדה לקווים מנחים" msgid "shortcuts.toggle-textpalette" msgstr "החלפת לוח טקסט" -msgid "shortcuts.toggle-visibility" -msgstr "החלפת מצב הצגה" - msgid "shortcuts.toggle-zoom-style" msgstr "החלפת סגנון תקריב" diff --git a/frontend/translations/hr.po b/frontend/translations/hr.po index 30e166fcc..24aecc15c 100644 --- a/frontend/translations/hr.po +++ b/frontend/translations/hr.po @@ -2405,9 +2405,6 @@ msgstr "Pričvrsti na guides" msgid "shortcuts.toggle-textpalette" msgstr "Promijeni paletu teksta" -msgid "shortcuts.toggle-visibility" -msgstr "Promijeni vidljivost" - msgid "shortcuts.toggle-zoom-style" msgstr "Promijeni stil zooma" diff --git a/frontend/translations/id.po b/frontend/translations/id.po index b54da37bb..aed9bfad0 100644 --- a/frontend/translations/id.po +++ b/frontend/translations/id.po @@ -2559,9 +2559,6 @@ msgstr "Tancap ke pemandu" msgid "shortcuts.toggle-textpalette" msgstr "Alih palet teks" -msgid "shortcuts.toggle-visibility" -msgstr "Alih keterlihatan" - msgid "shortcuts.toggle-zoom-style" msgstr "Alih gaya zum" diff --git a/frontend/translations/lv.po b/frontend/translations/lv.po index b798f414d..8f6ad1c27 100644 --- a/frontend/translations/lv.po +++ b/frontend/translations/lv.po @@ -2592,9 +2592,6 @@ msgstr "Pieķerties vadotnēm" msgid "shortcuts.toggle-textpalette" msgstr "Pārslēgt teksta paleti" -msgid "shortcuts.toggle-visibility" -msgstr "Pārslēgt redzamību" - msgid "shortcuts.toggle-zoom-style" msgstr "Pārslēgt tālummaiņas stilu" diff --git a/frontend/translations/pl.po b/frontend/translations/pl.po index bfe0c4581..e82a76644 100644 --- a/frontend/translations/pl.po +++ b/frontend/translations/pl.po @@ -2612,9 +2612,6 @@ msgstr "Przyciągaj do prowadnic" msgid "shortcuts.toggle-textpalette" msgstr "Przełącz paletę tekstu" -msgid "shortcuts.toggle-visibility" -msgstr "Przełącz widoczność" - msgid "shortcuts.toggle-zoom-style" msgstr "Przełącz sposób powiększania" diff --git a/frontend/translations/pt_BR.po b/frontend/translations/pt_BR.po index d6f75a8ca..16a7baf60 100644 --- a/frontend/translations/pt_BR.po +++ b/frontend/translations/pt_BR.po @@ -2567,9 +2567,6 @@ msgstr "Aderir as réguas" msgid "shortcuts.toggle-textpalette" msgstr "Mostrar/Esconder paleta de tipografias" -msgid "shortcuts.toggle-visibility" -msgstr "Alternar visibilidade" - msgid "shortcuts.toggle-zoom-style" msgstr "Alternar estilo de zoom" diff --git a/frontend/translations/pt_PT.po b/frontend/translations/pt_PT.po index 7d04a6f48..960d48567 100644 --- a/frontend/translations/pt_PT.po +++ b/frontend/translations/pt_PT.po @@ -2424,9 +2424,6 @@ msgstr "Ajustar às guias" msgid "shortcuts.toggle-textpalette" msgstr "Alternar paleta de texto" -msgid "shortcuts.toggle-visibility" -msgstr "Alternar visibilidade" - msgid "shortcuts.toggle-zoom-style" msgstr "Alternar estilo de zoom" diff --git a/frontend/translations/ro.po b/frontend/translations/ro.po index f16de1870..6913b9a10 100644 --- a/frontend/translations/ro.po +++ b/frontend/translations/ro.po @@ -2589,9 +2589,6 @@ msgstr "Fixare la ghiduri" msgid "shortcuts.toggle-textpalette" msgstr "Comutați paleta de text" -msgid "shortcuts.toggle-visibility" -msgstr "Comutați vizibilitatea" - msgid "shortcuts.toggle-zoom-style" msgstr "Comutați stilul zoomului" diff --git a/frontend/translations/tr.po b/frontend/translations/tr.po index 31300fe2b..13d08e552 100644 --- a/frontend/translations/tr.po +++ b/frontend/translations/tr.po @@ -2691,9 +2691,6 @@ msgstr "Kılavuzlara tuttur" msgid "shortcuts.toggle-textpalette" msgstr "Metin paletini değiştir" -msgid "shortcuts.toggle-visibility" -msgstr "Görünürlüğü değiştir" - msgid "shortcuts.toggle-zoom-style" msgstr "Yakınlaştırma şeklini değiştir" diff --git a/frontend/translations/zh_CN.po b/frontend/translations/zh_CN.po index f227f41e3..f2be6a428 100644 --- a/frontend/translations/zh_CN.po +++ b/frontend/translations/zh_CN.po @@ -2446,9 +2446,6 @@ msgstr "辅助线对齐" msgid "shortcuts.toggle-textpalette" msgstr "切换文本调色板" -msgid "shortcuts.toggle-visibility" -msgstr "切换可见度" - msgid "shortcuts.toggle-zoom-style" msgstr "切换缩放样式" From a2d1ce81209e1b3b4568c35af579e04093c6801b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 6 Jul 2023 12:43:59 +0200 Subject: [PATCH 09/23] :bug: Fix overlay position in open-overlay --- CHANGES.md | 3 ++- frontend/src/app/main/ui/viewer/shapes.cljs | 23 ++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5d11dac02..d09d084ae 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -63,7 +63,8 @@ - Fix search bar width on layer tab [Taiga #5445](https://tree.taiga.io/project/penpot/issue/5445) - Fix border radius values with decimals [Taiga #5283](https://tree.taiga.io/project/penpot/issue/5283) - Fix shortcuts translations not homogenized [Taiga #5141](https://tree.taiga.io/project/penpot/issue/5141) - +- Fix overlay manual position in nested boards [Taiga #5135](https://tree.taiga.io/project/penpot/issue/5135) + ### :arrow_up: Deps updates - Update google fonts catalog (at 2023/07/06) [Taiga #5592](https://tree.taiga.io/project/penpot/issue/5592) diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 619e0a159..4d279cd0f 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -59,8 +59,7 @@ :open-overlay (let [dest-frame-id (:destination interaction) - viewer-objects (deref (refs/get-viewer-objects)) - dest-frame (get viewer-objects dest-frame-id) + dest-frame (get objects dest-frame-id) relative-to-id (if (= :manual (:overlay-pos-type interaction)) (:id shape) ;; manual interactions are allways from "self" (:position-relative-to interaction)) @@ -71,7 +70,7 @@ relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame) position (ctsi/calc-overlay-position interaction shape - viewer-objects + objects relative-to-shape relative-to-base-frame dest-frame @@ -84,8 +83,8 @@ (:animation interaction))))) :toggle-overlay - (let [frame-id (:destination interaction) - dest-frame (get objects frame-id) + (let [dest-frame-id (:destination interaction) + dest-frame (get objects dest-frame-id) relative-to-id (if (= :manual (:overlay-pos-type interaction)) (:id shape) ;; manual interactions are allways from "self" (:position-relative-to interaction)) @@ -102,19 +101,19 @@ close-click-outside (:close-click-outside interaction) background-overlay (:background-overlay interaction)] - (when frame-id - (st/emit! (dv/toggle-overlay frame-id + (when dest-frame-id + (st/emit! (dv/toggle-overlay dest-frame-id position close-click-outside background-overlay (:animation interaction))))) :close-overlay - (let [frame-id (or (:destination interaction) - (if (= (:type shape) :frame) - (:id shape) - (:frame-id shape)))] - (st/emit! (dv/close-overlay frame-id (:animation interaction)))) + (let [dest-frame-id (or (:destination interaction) + (if (= (:type shape) :frame) + (:id shape) + (:frame-id shape)))] + (st/emit! (dv/close-overlay dest-frame-id (:animation interaction)))) :prev-screen (st/emit! (rt/nav-back-local)) From e563611c058436bffc7b4ccc3fe24af519f6ef7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Fri, 7 Jul 2023 10:21:07 +0200 Subject: [PATCH 10/23] :bug: Fix overlay close from an artboard --- CHANGES.md | 1 + frontend/src/app/main/ui/viewer/shapes.cljs | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index d09d084ae..6d7acc4a3 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -64,6 +64,7 @@ - Fix border radius values with decimals [Taiga #5283](https://tree.taiga.io/project/penpot/issue/5283) - Fix shortcuts translations not homogenized [Taiga #5141](https://tree.taiga.io/project/penpot/issue/5141) - Fix overlay manual position in nested boards [Taiga #5135](https://tree.taiga.io/project/penpot/issue/5135) +- Fix close overlay from a nested board [Taiga #5587](https://tree.taiga.io/project/penpot/issue/5587) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 4d279cd0f..7131bc88c 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -110,7 +110,8 @@ :close-overlay (let [dest-frame-id (or (:destination interaction) - (if (= (:type shape) :frame) + (if (and (= (:type shape) :frame) + (some #(= (:id %) (:id shape)) overlays)) (:id shape) (:frame-id shape)))] (st/emit! (dv/close-overlay dest-frame-id (:animation interaction)))) From 65598aa724df74113013eee5b8eb3e66e1dea7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Fri, 7 Jul 2023 17:31:11 +0200 Subject: [PATCH 11/23] :bug: Fix overlay position when it has shadow or blur --- CHANGES.md | 1 + common/src/app/common/geom/shapes/bounds.cljc | 1 + .../app/common/types/shape/interactions.cljc | 39 ++-- .../types_shape_interactions_test.cljc | 198 ++++++++++++------ frontend/src/app/main/data/viewer.cljs | 9 +- frontend/src/app/main/ui/shapes/frame.cljs | 3 +- frontend/src/app/main/ui/viewer.cljs | 24 +++ .../app/main/ui/viewer/inspect/render.cljs | 3 +- .../src/app/main/ui/viewer/interactions.cljs | 10 +- frontend/src/app/main/ui/viewer/shapes.cljs | 56 +++-- 10 files changed, 242 insertions(+), 102 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 6d7acc4a3..44d750ebd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -65,6 +65,7 @@ - Fix shortcuts translations not homogenized [Taiga #5141](https://tree.taiga.io/project/penpot/issue/5141) - Fix overlay manual position in nested boards [Taiga #5135](https://tree.taiga.io/project/penpot/issue/5135) - Fix close overlay from a nested board [Taiga #5587](https://tree.taiga.io/project/penpot/issue/5587) +- Fix overlay position when it has shadow or blur [Taiga #4752](https://tree.taiga.io/project/penpot/issue/4752) ### :arrow_up: Deps updates diff --git a/common/src/app/common/geom/shapes/bounds.cljc b/common/src/app/common/geom/shapes/bounds.cljc index 9bb6686f6..92139415f 100644 --- a/common/src/app/common/geom/shapes/bounds.cljc +++ b/common/src/app/common/geom/shapes/bounds.cljc @@ -147,6 +147,7 @@ :else (cph/reduce-objects objects + (fn [shape] (and (d/not-empty? (:shapes shape)) (or (not (cph/frame-shape? shape)) diff --git a/common/src/app/common/types/shape/interactions.cljc b/common/src/app/common/types/shape/interactions.cljc index 5bfa90681..61ef1ebea 100644 --- a/common/src/app/common/types/shape/interactions.cljc +++ b/common/src/app/common/types/shape/interactions.cljc @@ -526,37 +526,46 @@ overlay-position {:x (- (:x overlay-position) (:x relative-to-adjusted-to-base-frame)) :y (- (:y overlay-position) (:y relative-to-adjusted-to-base-frame))})] + (case (:overlay-pos-type interaction) :center - (gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2)) - (+ (:y base-position) (/ (- (:height relative-to-shape-size) (:height overlay-size)) 2))) + [(gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2)) + (+ (:y base-position) (/ (- (:height relative-to-shape-size) (:height overlay-size)) 2))) + [:center :center]] :top-left - (gpt/point (:x base-position) (:y base-position)) + [(gpt/point (:x base-position) (:y base-position)) + [:top :left]] :top-right - (gpt/point (+ (:x base-position) (- (:width relative-to-shape-size) (:width overlay-size))) - (:y base-position)) + [(gpt/point (+ (:x base-position) (- (:width relative-to-shape-size) (:width overlay-size))) + (:y base-position)) + [:top :right]] :top-center - (gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2)) - (:y base-position)) + [(gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2)) + (:y base-position)) + [:top :center]] :bottom-left - (gpt/point (:x base-position) - (+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size)))) + [(gpt/point (:x base-position) + (+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size)))) + [:bottom :left]] :bottom-right - (gpt/point (+ (:x base-position) (- (:width relative-to-shape-size) (:width overlay-size))) - (+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size)))) + [(gpt/point (+ (:x base-position) (- (:width relative-to-shape-size) (:width overlay-size))) + (+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size)))) + [:bottom :right]] :bottom-center - (gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2)) - (+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size)))) + [(gpt/point (+ (:x base-position) (/ (- (:width relative-to-shape-size) (:width overlay-size)) 2)) + (+ (:y base-position) (- (:height relative-to-shape-size) (:height overlay-size)))) + [:bottom :center]] :manual - (gpt/point (+ (:x base-position) (:x overlay-position)) - (+ (:y base-position) (:y overlay-position)))))))) + [(gpt/point (+ (:x base-position) (:x overlay-position)) + (+ (:y base-position) (:y overlay-position))) + [:top :left]]))))) (defn has-animation? [interaction] diff --git a/common/test/common_tests/types_shape_interactions_test.cljc b/common/test/common_tests/types_shape_interactions_test.cljc index 4411d86f1..34fd3d47b 100644 --- a/common/test/common_tests/types_shape_interactions_test.cljc +++ b/common/test/common_tests/types_shape_interactions_test.cljc @@ -324,209 +324,275 @@ interaction-rect (ctsi/set-position-relative-to interaction (:id rect))] (t/testing "Overlay top-left relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 0)) - (t/is (= (:y overlay-pos) 0)))) + (t/is (= (:y overlay-pos) 0)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))) (t/testing "Overlay top-center relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 0)))) + (t/is (= (:y overlay-pos) 0)) + (t/is (= snap-v :top)) + (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :top-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 70)) - (t/is (= (:y overlay-pos) 0)))) + (t/is (= (:y overlay-pos) 0)) + (t/is (= snap-v :top)) + (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 0)) - (t/is (= (:y overlay-pos) 80)))) + (t/is (= (:y overlay-pos) 80)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 80)))) + (t/is (= (:y overlay-pos) 80)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :bottom-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 70)) - (t/is (= (:y overlay-pos) 80)))) + (t/is (= (:y overlay-pos) 80)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :right)))) (t/testing "Overlay center relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 40)))) + (t/is (= (:y overlay-pos) 40)) + (t/is (= snap-v :center)) + (t/is (= snap-h :center)))) (t/testing "Overlay manual relative to auto" (let [i2 (ctsi/set-overlay-pos-type interaction-auto :center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 40)))) + (t/is (= (:y overlay-pos) 40)) + (t/is (= snap-v :center)) + (t/is (= snap-h :center)))) (t/testing "Overlay manual relative to auto" (let [i2 (-> interaction-auto (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 17)) - (t/is (= (:y overlay-pos) 67)))) + (t/is (= (:y overlay-pos) 67)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))) (t/testing "Overlay top-left relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 5)) - (t/is (= (:y overlay-pos) 5)))) + (t/is (= (:y overlay-pos) 5)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))) (t/testing "Overlay top-center relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 40)) - (t/is (= (:y overlay-pos) 5)))) + (t/is (= (:y overlay-pos) 5)) + (t/is (= snap-v :top)) + (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :top-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 75)) - (t/is (= (:y overlay-pos) 5)))) + (t/is (= (:y overlay-pos) 5)) + (t/is (= snap-v :top)) + (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 5)) - (t/is (= (:y overlay-pos) 85)))) + (t/is (= (:y overlay-pos) 85)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 40)) - (t/is (= (:y overlay-pos) 85)))) + (t/is (= (:y overlay-pos) 85)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :bottom-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 75)) - (t/is (= (:y overlay-pos) 85)))) + (t/is (= (:y overlay-pos) 85)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :right)))) (t/testing "Overlay center relative to base-frame" (let [i2 (ctsi/set-overlay-pos-type interaction-base-frame :center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 40)) - (t/is (= (:y overlay-pos) 45)))) + (t/is (= (:y overlay-pos) 45)) + (t/is (= snap-v :center)) + (t/is (= snap-h :center)))) (t/testing "Overlay manual relative to base-frame" (let [i2 (-> interaction-base-frame (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) - overlay-pos (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects base-frame base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 17)) - (t/is (= (:y overlay-pos) 67)))) + (t/is (= (:y overlay-pos) 67)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))) (t/testing "Overlay top-left relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 15)))) + (t/is (= (:y overlay-pos) 15)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))) (t/testing "Overlay top-center relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 15)))) + (t/is (= (:y overlay-pos) 15)) + (t/is (= snap-v :top)) + (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 15)))) + (t/is (= (:y overlay-pos) 15)) + (t/is (= snap-v :top)) + (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 45)))) + (t/is (= (:y overlay-pos) 45)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 45)))) + (t/is (= (:y overlay-pos) 45)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :bottom-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 45)))) + (t/is (= (:y overlay-pos) 45)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :right)))) (t/testing "Overlay center relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 30)))) + (t/is (= (:y overlay-pos) 30)) + (t/is (= snap-v :center)) + (t/is (= snap-h :center)))) (t/testing "Overlay manual relative to popup" (let [i2 (-> interaction-popup (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 27)) - (t/is (= (:y overlay-pos) 77)))) + (t/is (= (:y overlay-pos) 77)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))) (t/testing "Overlay top-left relative to popup" (let [i2 (ctsi/set-overlay-pos-type interaction-popup :top-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects popup base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 15)))) + (t/is (= (:y overlay-pos) 15)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))) (t/testing "Overlay top-center relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :top-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 15)))) + (t/is (= (:y overlay-pos) 15)) + (t/is (= snap-v :top)) + (t/is (= snap-h :center)))) (t/testing "Overlay top-right relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :top-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 15)))) + (t/is (= (:y overlay-pos) 15)) + (t/is (= snap-v :top)) + (t/is (= snap-h :right)))) (t/testing "Overlay bottom-left relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :bottom-left base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 15)) - (t/is (= (:y overlay-pos) 45)))) + (t/is (= (:y overlay-pos) 45)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :left)))) (t/testing "Overlay bottom-center relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :bottom-center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 45)))) + (t/is (= (:y overlay-pos) 45)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :center)))) (t/testing "Overlay bottom-right relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :bottom-right base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 35)) - (t/is (= (:y overlay-pos) 45)))) + (t/is (= (:y overlay-pos) 45)) + (t/is (= snap-v :bottom)) + (t/is (= snap-h :right)))) (t/testing "Overlay center relative to rect" (let [i2 (ctsi/set-overlay-pos-type interaction-rect :center base-frame objects) - overlay-pos (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 25)) - (t/is (= (:y overlay-pos) 30)))) + (t/is (= (:y overlay-pos) 30)) + (t/is (= snap-v :center)) + (t/is (= snap-h :center)))) (t/testing "Overlay manual relative to rect" (let [i2 (-> interaction-rect (ctsi/set-overlay-pos-type :manual base-frame objects) (ctsi/set-overlay-position (gpt/point 12 62))) - overlay-pos (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] + [overlay-pos [snap-v snap-h]] (ctsi/calc-overlay-position i2 rect objects rect base-frame overlay-frame frame-offset)] (t/is (= (:x overlay-pos) 17)) - (t/is (= (:y overlay-pos) 67)))))) + (t/is (= (:y overlay-pos) 67)) + (t/is (= snap-v :top)) + (t/is (= snap-h :left)))))) (t/deftest animation-checks diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index f4675beaa..40df6b66c 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -542,13 +542,14 @@ ;; --- Overlays (defn- open-overlay* - [state frame position close-click-outside background-overlay animation] + [state frame position snap-to close-click-outside background-overlay animation] (cond-> state :always (update :viewer-overlays conj {:frame frame :id (:id frame) :position position + :snap-to snap-to :close-click-outside close-click-outside :background-overlay background-overlay :animation animation}) @@ -571,7 +572,7 @@ :animation animation}))) (defn open-overlay - [frame-id position close-click-outside background-overlay animation] + [frame-id position snap-to close-click-outside background-overlay animation] (dm/assert! (uuid? frame-id)) (dm/assert! (gpt/point? position)) (dm/assert! (or (nil? close-click-outside) @@ -593,6 +594,7 @@ (open-overlay* state frame position + snap-to close-click-outside background-overlay animation) @@ -600,7 +602,7 @@ (defn toggle-overlay - [frame-id position close-click-outside background-overlay animation] + [frame-id position snap-to close-click-outside background-overlay animation] (dm/assert! (uuid? frame-id)) (dm/assert! (gpt/point? position)) (dm/assert! (or (nil? close-click-outside) @@ -623,6 +625,7 @@ (open-overlay* state frame position + snap-to close-click-outside background-overlay animation) diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 8a71a26ac..42eccf32b 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -142,5 +142,6 @@ [:> frame-container props [:g.frame-children {:opacity (:opacity shape)} (for [item childs] - [:& shape-wrapper {:key (dm/str (:id item)) :shape item}])]]))) + (when (:id item) + [:& shape-wrapper {:key (dm/str (:id item)) :shape item}]))]]))) diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 8d1e315ba..ad8e0f594 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -50,6 +50,8 @@ (l/derived :viewer-overlays st/state)) (defn- calculate-size + "Calculate the total size we must reserve for the frame, including possible paddings + added because shadows or blur." [objects frame zoom] (let [{:keys [x y width height]} (gsb/get-object-bounds objects frame)] {:base-width width @@ -60,6 +62,23 @@ :height (* height zoom) :vbox (dm/fmt "% % % %" 0 0 width height)})) +(defn calculate-delta + "Calculate the displacement we need to apply so that the original selrect appears in the + same position as if it had no extra paddings, depending on the side the frame will + be snapped to." + [size selrect [snap-v snap-h] zoom] + (let [delta-x (case snap-h + :left (- (:x1 selrect) (:x size)) + :right (- (:x2 selrect) (+ (:x size) (/ (:width size) zoom))) + :center (- (/ (- (:width selrect) (/ (:width size) zoom)) 2) + (- (:x size) (:x1 selrect)))) + delta-y (case snap-v + :top (- (:y1 selrect) (:y size)) + :bottom (- (:y2 selrect) (+ (:y size) (/ (:height size) zoom))) + :center (- (/ (- (:height selrect) (/ (:height size) zoom)) 2) + (- (:y size) (:y1 selrect))))] + (gpt/point (* delta-x zoom) (* delta-y zoom)))) + (defn- calculate-wrapper [size1 size2 zoom] (cond @@ -113,6 +132,10 @@ (mf/with-memo [page overlay zoom] (calculate-size (:objects page) (:frame overlay) zoom)) + delta + (mf/with-memo [size overlay-frame overlay zoom] + (calculate-delta size (:selrect overlay-frame) (:snap-to overlay) zoom)) + on-click (mf/use-fn (mf/deps overlay close-click-outside?) @@ -145,6 +168,7 @@ :base-frame frame :frame-offset overlay-position :size size + :delta delta :page page :interactions-mode interactions-mode}]]])) diff --git a/frontend/src/app/main/ui/viewer/inspect/render.cljs b/frontend/src/app/main/ui/viewer/inspect/render.cljs index da8e79d80..01adf3e35 100644 --- a/frontend/src/app/main/ui/viewer/inspect/render.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/render.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.viewer.inspect.render "The main container for a frame in inspect mode" (:require + [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.common.pages.helpers :as cph] [app.main.data.viewer :as dv] @@ -186,7 +187,7 @@ (mf/defc render-frame-svg [{:keys [page frame local size]}] (let [objects (mf/with-memo [page frame size] - (prepare-objects frame size (:objects page))) + (prepare-objects frame size (gpt/point 0 0) (:objects page))) ;; Retrieve frame again with correct modifier frame (get objects (:id frame)) diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index 6e2e6320e..a3419e419 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -28,9 +28,10 @@ [rumext.v2 :as mf])) (defn prepare-objects - [frame size objects] + [frame size delta objects] (let [frame-id (:id frame) vector (-> (gpt/point (:x size) (:y size)) + (gpt/add delta) (gpt/negate)) update-fn #(d/update-when %1 %2 gsh/transform-shape (ctm/move-modifiers vector))] (->> (cph/get-children-ids objects frame-id) @@ -46,6 +47,7 @@ base (unchecked-get props "base") offset (unchecked-get props "offset") size (unchecked-get props "size") + delta (or (unchecked-get props "delta") (gpt/point 0 0)) vbox (:vbox size) @@ -67,7 +69,7 @@ (map (d/getf (:objects page))) (concat [frame]) (d/index-by :id) - (prepare-objects frame size))) + (prepare-objects frame size delta))) wrapper-fixed (mf/with-memo [page frame size] (shapes/frame-container-factory (calculate-objects fixed-ids))) @@ -121,6 +123,7 @@ mode (h/use-equal-memo (unchecked-get props "interactions-mode")) offset (h/use-equal-memo (unchecked-get props "frame-offset")) size (h/use-equal-memo (unchecked-get props "size")) + delta (unchecked-get props "delta") page (unchecked-get props "page") frame (unchecked-get props "frame") @@ -163,7 +166,8 @@ :frame frame :base base :offset offset - :size size}])) + :size size + :delta delta}])) (mf/defc flows-menu {::mf/wrap [mf/memo]} diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index 7131bc88c..ccc6663ce 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -61,14 +61,16 @@ (let [dest-frame-id (:destination interaction) dest-frame (get objects dest-frame-id) relative-to-id (if (= :manual (:overlay-pos-type interaction)) - (:id shape) ;; manual interactions are allways from "self" + (if (= (:type shape) :frame) ;; manual interactions are always from "self" + (:frame-id shape) + (:id shape)) (:position-relative-to interaction)) relative-to-shape (or (get objects relative-to-id) base-frame) close-click-outside (:close-click-outside interaction) background-overlay (:background-overlay interaction) overlays-ids (set (map :id overlays)) relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame) - position (ctsi/calc-overlay-position interaction + [position snap-to] (ctsi/calc-overlay-position interaction shape objects relative-to-shape @@ -78,6 +80,7 @@ (when dest-frame-id (st/emit! (dv/open-overlay dest-frame-id position + snap-to close-click-outside background-overlay (:animation interaction))))) @@ -86,12 +89,14 @@ (let [dest-frame-id (:destination interaction) dest-frame (get objects dest-frame-id) relative-to-id (if (= :manual (:overlay-pos-type interaction)) - (:id shape) ;; manual interactions are allways from "self" - (:position-relative-to interaction)) + (if (= (:type shape) :frame) ;; manual interactions are always from "self" + (:frame-id shape) + (:id shape)) + (:position-relative-to interaction)) relative-to-shape (or (get objects relative-to-id) base-frame) overlays-ids (set (map :id overlays)) relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame) - position (ctsi/calc-overlay-position interaction + [position snap-to] (ctsi/calc-overlay-position interaction shape objects relative-to-shape @@ -104,6 +109,7 @@ (when dest-frame-id (st/emit! (dv/toggle-overlay dest-frame-id position + snap-to close-click-outside background-overlay (:animation interaction))))) @@ -136,29 +142,49 @@ (st/emit! (dv/close-overlay frame-id))) :toggle-overlay - (let [frame-id (:destination interaction) - position (:overlay-position interaction) - close-click-outside (:close-click-outside interaction) - background-overlay (:background-overlay interaction)] - (when frame-id - (st/emit! (dv/toggle-overlay frame-id + (let [dest-frame-id (:destination interaction) + dest-frame (get objects dest-frame-id) + relative-to-id (if (= :manual (:overlay-pos-type interaction)) + (if (= (:type shape) :frame) ;; manual interactions are always from "self" + (:frame-id shape) + (:id shape)) + (:position-relative-to interaction)) + relative-to-shape (or (get objects relative-to-id) base-frame) + overlays-ids (set (map :id overlays)) + relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame) + [position snap-to] (ctsi/calc-overlay-position interaction + shape + objects + relative-to-shape + relative-to-base-frame + dest-frame + frame-offset) + + close-click-outside (:close-click-outside interaction) + background-overlay (:background-overlay interaction)] + (when dest-frame-id + (st/emit! (dv/toggle-overlay dest-frame-id position + snap-to close-click-outside background-overlay (:animation interaction))))) + :close-overlay (let [dest-frame-id (:destination interaction) dest-frame (get objects dest-frame-id) relative-to-id (if (= :manual (:overlay-pos-type interaction)) - (:id shape) ;; manual interactions are allways from "self" + (if (= (:type shape) :frame) ;; manual interactions are always from "self" + (:frame-id shape) + (:id shape)) (:position-relative-to interaction)) relative-to-shape (or (get objects relative-to-id) base-frame) close-click-outside (:close-click-outside interaction) background-overlay (:background-overlay interaction) overlays-ids (set (map :id overlays)) relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame) - position (ctsi/calc-overlay-position interaction + [position snap-to] (ctsi/calc-overlay-position interaction shape objects relative-to-shape @@ -168,6 +194,7 @@ (when dest-frame-id (st/emit! (dv/open-overlay dest-frame-id position + snap-to close-click-outside background-overlay (:animation interaction))))) @@ -266,6 +293,9 @@ svg-element? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag]))) + ;; _ (js/console.log "======" (:name shape)) + ;; _ (js/console.log "shape" (clj->js shape)) + ;; _ (js/console.log "frame-offset" (clj->js frame-offset)) on-pointer-down (mf/use-fn (mf/deps shape base-frame frame-offset objects) #(on-pointer-down % shape base-frame frame-offset objects overlays)) From 491251f5ceb1c168b91a9ccea32a9c182636212d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 11 Jul 2023 20:15:20 +0200 Subject: [PATCH 12/23] :bug: Fix overlay position with elements fixed when scrolling --- CHANGES.md | 1 + .../app/common/types/shape/interactions.cljc | 9 ++- .../src/app/main/ui/viewer/interactions.cljs | 18 ++++-- frontend/src/app/main/ui/viewer/shapes.cljs | 59 ++++++++++--------- 4 files changed, 48 insertions(+), 39 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 44d750ebd..079eb6291 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -66,6 +66,7 @@ - Fix overlay manual position in nested boards [Taiga #5135](https://tree.taiga.io/project/penpot/issue/5135) - Fix close overlay from a nested board [Taiga #5587](https://tree.taiga.io/project/penpot/issue/5587) - Fix overlay position when it has shadow or blur [Taiga #4752](https://tree.taiga.io/project/penpot/issue/4752) +- Fix overlay position when there are elements fixed when scrolling [Taiga #4383](https://tree.taiga.io/project/penpot/issue/4383) ### :arrow_up: Deps updates diff --git a/common/src/app/common/types/shape/interactions.cljc b/common/src/app/common/types/shape/interactions.cljc index 61ef1ebea..162222c30 100644 --- a/common/src/app/common/types/shape/interactions.cljc +++ b/common/src/app/common/types/shape/interactions.cljc @@ -495,8 +495,7 @@ "expected compatible interaction map" (has-overlay-opts interaction)) - (let [ - ;; When the interactive item is inside a nested frame we need to add to the offset the position + (let [;; When the interactive item is inside a nested frame we need to add to the offset the position ;; of the parent-frame otherwise the position won't match shape-frame (cph/get-frame objects shape) @@ -505,10 +504,10 @@ (cph/root-frame? shape-frame) (cph/root? shape-frame)) frame-offset - (gpt/add frame-offset (gpt/point shape-frame))) - ] + (gpt/add frame-offset (gpt/point shape-frame)))] + (if (nil? dest-frame) - (gpt/point 0 0) + [(gpt/point 0 0) [:top :left]] (let [overlay-size (gsb/get-object-bounds objects dest-frame) base-frame-size (:selrect base-frame) relative-to-shape-size (:selrect relative-to-shape) diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index a3419e419..deab4db36 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -71,18 +71,24 @@ (d/index-by :id) (prepare-objects frame size delta))) - wrapper-fixed (mf/with-memo [page frame size] - (shapes/frame-container-factory (calculate-objects fixed-ids))) + objects-fixed (mf/with-memo [fixed-ids page frame size delta] + (calculate-objects fixed-ids)) - objects-not-fixed (mf/with-memo [page frame size] + objects-not-fixed (mf/with-memo [not-fixed-ids page frame size delta] (calculate-objects not-fixed-ids)) + all-objects (mf/with-memo [objects-fixed objects-not-fixed] + (merge objects-fixed objects-not-fixed)) + + wrapper-fixed (mf/with-memo [page frame size] + (shapes/frame-container-factory objects-fixed all-objects)) + wrapper-not-fixed (mf/with-memo [objects-not-fixed] - (shapes/frame-container-factory objects-not-fixed)) + (shapes/frame-container-factory objects-not-fixed all-objects)) ;; Retrieve frames again with correct modifier - frame (get objects-not-fixed (:id frame)) - base (get objects-not-fixed (:id base)) + frame (get all-objects (:id frame)) + base (get all-objects (:id base)) non-delay-interactions (->> (:interactions frame) (filterv #(not= (:event-type %) :after-delay))) diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index ccc6663ce..13f9c1048 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -92,7 +92,7 @@ (if (= (:type shape) :frame) ;; manual interactions are always from "self" (:frame-id shape) (:id shape)) - (:position-relative-to interaction)) + (:position-relative-to interaction)) relative-to-shape (or (get objects relative-to-id) base-frame) overlays-ids (set (map :id overlays)) relative-to-base-frame (find-relative-to-base-frame relative-to-shape objects overlays-ids base-frame) @@ -285,6 +285,7 @@ childs (unchecked-get props "childs") frame (unchecked-get props "frame") objects (unchecked-get props "objects") + all-objects (or (unchecked-get props "all-objects") objects) base-frame (mf/use-ctx base-frame-ctx) frame-offset (mf/use-ctx frame-offset-ctx) interactions-show? (mf/deref viewer-interactions-show?) @@ -293,25 +294,25 @@ svg-element? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag]))) - ;; _ (js/console.log "======" (:name shape)) - ;; _ (js/console.log "shape" (clj->js shape)) - ;; _ (js/console.log "frame-offset" (clj->js frame-offset)) + ;; The objects parameter has the shapes that we must draw. It may be a subset of + ;; all-objects in some cases (e.g. if there are fixed elements). But for interactions + ;; handling we need access to all objects inside the page. + on-pointer-down - (mf/use-fn (mf/deps shape base-frame frame-offset objects) - #(on-pointer-down % shape base-frame frame-offset objects overlays)) + (mf/use-fn (mf/deps shape base-frame frame-offset all-objects) + #(on-pointer-down % shape base-frame frame-offset all-objects overlays)) on-pointer-up - (mf/use-fn (mf/deps shape base-frame frame-offset objects) - #(on-pointer-up % shape base-frame frame-offset objects overlays)) + (mf/use-fn (mf/deps shape base-frame frame-offset all-objects) + #(on-pointer-up % shape base-frame frame-offset all-objects overlays)) on-pointer-enter - (mf/use-fn (mf/deps shape base-frame frame-offset objects) - #(on-pointer-enter % shape base-frame frame-offset objects overlays)) + (mf/use-fn (mf/deps shape base-frame frame-offset all-objects) + #(on-pointer-enter % shape base-frame frame-offset all-objects overlays)) on-pointer-leave - (mf/use-fn (mf/deps shape base-frame frame-offset objects) - #(on-pointer-leave % shape base-frame frame-offset objects overlays))] - + (mf/use-fn (mf/deps shape base-frame frame-offset all-objects) + #(on-pointer-leave % shape base-frame frame-offset all-objects overlays))] (mf/with-effect [] (let [sems (on-load shape base-frame frame-offset objects overlays)] @@ -380,8 +381,8 @@ (declare shape-container-factory) (defn frame-container-factory - [objects] - (let [shape-container (shape-container-factory objects) + [objects all-objects] + (let [shape-container (shape-container-factory objects all-objects) frame-wrapper (frame-wrapper shape-container)] (mf/fnc frame-container {::mf/wrap-props false} @@ -391,13 +392,14 @@ props (obj/merge! #js {} props #js {:shape shape :childs childs - :objects objects})] + :objects objects + :all-objects all-objects})] [:> frame-wrapper props])))) (defn group-container-factory - [objects] - (let [shape-container (shape-container-factory objects) + [objects all-objects] + (let [shape-container (shape-container-factory objects all-objects) group-wrapper (group-wrapper shape-container)] (mf/fnc group-container {::mf/wrap-props false} @@ -410,8 +412,8 @@ [:> group-wrapper props]))))) (defn bool-container-factory - [objects] - (let [shape-container (shape-container-factory objects) + [objects all-objects] + (let [shape-container (shape-container-factory objects all-objects) bool-wrapper (bool-wrapper shape-container)] (mf/fnc bool-container {::mf/wrap-props false} @@ -424,8 +426,8 @@ [:> bool-wrapper props])))) (defn svg-raw-container-factory - [objects] - (let [shape-container (shape-container-factory objects) + [objects all-objects] + (let [shape-container (shape-container-factory objects all-objects) svg-raw-wrapper (svg-raw-wrapper shape-container)] (mf/fnc svg-raw-container {::mf/wrap-props false} @@ -437,7 +439,7 @@ [:> svg-raw-wrapper props])))) (defn shape-container-factory - [objects] + [objects all-objects] (let [path-wrapper (path-wrapper) text-wrapper (text-wrapper) rect-wrapper (rect-wrapper) @@ -452,26 +454,27 @@ group-container (mf/with-memo [objects] - (group-container-factory objects)) + (group-container-factory objects all-objects)) frame-container (mf/with-memo [objects] - (frame-container-factory objects)) + (frame-container-factory objects all-objects)) bool-container (mf/with-memo [objects] - (bool-container-factory objects)) + (bool-container-factory objects all-objects)) svg-raw-container (mf/with-memo [objects] - (svg-raw-container-factory objects))] + (svg-raw-container-factory objects all-objects))] (when (and shape (not (:hidden shape))) (let [shape (-> shape #_(gsh/transform-shape) (gsh/translate-to-frame frame)) opts #js {:shape shape - :objects objects}] + :objects objects + :all-objects all-objects}] (case (:type shape) :frame [:> frame-container opts] :text [:> text-wrapper opts] From 7e0a612818cac700decb30e9b20d5815b571e6d1 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 10 Jul 2023 13:42:33 +0200 Subject: [PATCH 13/23] :bug: Fix problem when sliding color picker in selected-colors --- CHANGES.md | 1 + .../options/menus/color_selection.cljs | 28 +++++++------------ 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 079eb6291..ab87123b5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -67,6 +67,7 @@ - Fix close overlay from a nested board [Taiga #5587](https://tree.taiga.io/project/penpot/issue/5587) - Fix overlay position when it has shadow or blur [Taiga #4752](https://tree.taiga.io/project/penpot/issue/4752) - Fix overlay position when there are elements fixed when scrolling [Taiga #4383](https://tree.taiga.io/project/penpot/issue/4383) +- Fix problem when sliding color picker in selected-colors [#3150](https://github.com/penpot/penpot/issues/3150) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs index 857e60da5..333103028 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/color_selection.cljs @@ -75,7 +75,7 @@ (d/without-nils {:color (str/lower (dm/get-in shadow [:color :color])) :opacity (dm/get-in shadow [:color :opacity]) :gradient (dm/get-in shadow [:color :gradient])}))] - + {:attrs attrs :prop :shadow @@ -157,41 +157,33 @@ expand-color (mf/use-state false) grouped-colors* (mf/use-var nil) - prev-color* (mf/use-var nil) + prev-colors* (mf/use-var []) on-change (mf/use-fn (fn [new-color old-color from-picker?] - (let [old-color (-> old-color - (dissoc :name) - (dissoc :path) - (d/without-nils)) - - prev-color (when @prev-color* - (-> @prev-color* - (dissoc :name) - (dissoc :path) - (d/without-nils))) + (let [old-color (-> old-color (dissoc :name :path) d/without-nils) ;; When dragging on the color picker sometimes all the shapes hasn't updated the color to the prev value so we need this extra calculation shapes-by-old-color (get @grouped-colors* old-color) + prev-color (d/seek #(get @grouped-colors* %) @prev-colors*) shapes-by-prev-color (get @grouped-colors* prev-color) shapes-by-color (or shapes-by-prev-color shapes-by-old-color)] (when from-picker? - (reset! prev-color* new-color)) + (swap! prev-colors* conj (-> new-color (dissoc :name :path) d/without-nils))) (st/emit! (dc/change-color-in-selected new-color shapes-by-color (or prev-color old-color)))))) on-open (mf/use-fn (fn [] - (reset! prev-color* nil))) + (reset! prev-colors* []))) on-close (mf/use-fn (fn [] - (reset! prev-color* nil))) + (reset! prev-colors* []))) on-detach (mf/use-fn @@ -217,7 +209,7 @@ [:div.element-set-content [:div.selected-colors (for [[index color] (d/enumerate (take 3 library-colors))] - [:& color-row {:key (dm/str "library-color-" index) + [:& color-row {:key (dm/str "library-color-" (:color color)) :color color :index index :on-detach on-detach @@ -231,7 +223,7 @@ [:span.text (tr "workspace.options.more-lib-colors")]]) (when @expand-lib-color (for [[index color] (d/enumerate (drop 3 library-colors))] - [:& color-row {:key (dm/str "library-color-" index) + [:& color-row {:key (dm/str "library-color-" (:color color)) :color color :index index :on-detach on-detach @@ -255,7 +247,7 @@ [:span.text (tr "workspace.options.more-colors")]]) (when @expand-color (for [[index color] (d/enumerate (drop 3 colors))] - [:& color-row {:key (dm/str "color-" index) + [:& color-row {:key (dm/str "color-" (:color color)) :color color :index index :select-only select-only From 66559d3ce34e9ecd02df09cc9b8b01c3add8e2ac Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 10:55:17 +0200 Subject: [PATCH 14/23] :bug: Fix error screen on image upload failure --- CHANGES.md | 1 + frontend/src/app/main/data/workspace/media.cljs | 4 +++- frontend/translations/en.po | 3 +++ frontend/translations/es.po | 3 +++ 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index ab87123b5..379b072f2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -68,6 +68,7 @@ - Fix overlay position when it has shadow or blur [Taiga #4752](https://tree.taiga.io/project/penpot/issue/4752) - Fix overlay position when there are elements fixed when scrolling [Taiga #4383](https://tree.taiga.io/project/penpot/issue/4383) - Fix problem when sliding color picker in selected-colors [#3150](https://github.com/penpot/penpot/issues/3150) +- Fix error screen on upload image error [Taiga #5608](https://tree.taiga.io/project/penpot/issue/5608) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/data/workspace/media.cljs b/frontend/src/app/main/data/workspace/media.cljs index 424899ad8..b7226dd99 100644 --- a/frontend/src/app/main/data/workspace/media.cljs +++ b/frontend/src/app/main/data/workspace/media.cljs @@ -181,7 +181,9 @@ (on-error error) :else - (rx/throw error))))] + (do + (.error js/console "ERROR" error) + (rx/of (msg/error (tr "errors.cannot-upload")))))))] (ptk/reify ::process-media-objects ptk/WatchEvent diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 7c5e2f162..13b41ee30 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -1017,6 +1017,9 @@ msgstr "Email or password is incorrect." msgid "errors.wrong-old-password" msgstr "Old password is incorrect" +msgid "errors.cannot-upload" +msgstr "Cannot upload the media file." + #: src/app/main/ui/settings/feedback.cljs msgid "feedback.description" msgstr "Description" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index dcdd6727b..8076484d0 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1057,6 +1057,9 @@ msgstr "El email o la contraseña son incorrectos." msgid "errors.wrong-old-password" msgstr "La contraseña anterior no es correcta" +msgid "errors.cannot-upload" +msgstr "No se puede subir el fichero" + #: src/app/main/ui/settings/feedback.cljs msgid "feedback.description" msgstr "Descripción" From 5072c903c5aaa4fb1497030fedc7c501879abe0a Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 13:29:15 +0200 Subject: [PATCH 15/23] :bug: Fix bad frame-id for certain componentes --- CHANGES.md | 1 + common/src/app/common/pages/common.cljc | 2 +- common/src/app/common/pages/migrations.cljc | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 379b072f2..8f82cff62 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -69,6 +69,7 @@ - Fix overlay position when there are elements fixed when scrolling [Taiga #4383](https://tree.taiga.io/project/penpot/issue/4383) - Fix problem when sliding color picker in selected-colors [#3150](https://github.com/penpot/penpot/issues/3150) - Fix error screen on upload image error [Taiga #5608](https://tree.taiga.io/project/penpot/issue/5608) +- Fix bad frame-id for certain componentes [#3205](https://github.com/penpot/penpot/issues/3205) ### :arrow_up: Deps updates diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index 49e922632..e0443616b 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -12,7 +12,7 @@ [app.common.schema :as sm] [app.common.uuid :as uuid])) -(def file-version 20) +(def file-version 21) (def default-color clr/gray-20) (def root uuid/zero) diff --git a/common/src/app/common/pages/migrations.cljc b/common/src/app/common/pages/migrations.cljc index 510423710..72205dd4c 100644 --- a/common/src/app/common/pages/migrations.cljc +++ b/common/src/app/common/pages/migrations.cljc @@ -436,7 +436,7 @@ (update :pages-index update-vals update-container) (update :components update-vals update-container)))) -(defmethod migrate 20 +(defmethod migrate 21 [data] (letfn [(update-object [objects object] (let [frame-id (:frame-id object) From 9f121cb38b0566466420e5424a60595a0165f260 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 13:34:26 +0200 Subject: [PATCH 16/23] :bug: Fix problem with comments not sticking --- frontend/src/app/main/ui/workspace/left_toolbar.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/left_toolbar.cljs b/frontend/src/app/main/ui/workspace/left_toolbar.cljs index c0b4bce21..6439725af 100644 --- a/frontend/src/app/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/app/main/ui/workspace/left_toolbar.cljs @@ -85,7 +85,7 @@ dw/clear-edition-mode) ;; Delay so anything that launched :interrupt can finish - (st/emit! 100 (dw/select-for-drawing tool))))) + (ts/schedule 100 #(st/emit! (dw/select-for-drawing tool)))))) toggle-text-palette (mf/use-fn From 5631204567393ab22ef7d09c3712c8328e724f94 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 14:43:30 +0200 Subject: [PATCH 17/23] :bug: Fix paste elements at bottom of frame --- CHANGES.md | 1 + frontend/src/app/main/data/workspace.cljs | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8f82cff62..5617a320b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -70,6 +70,7 @@ - Fix problem when sliding color picker in selected-colors [#3150](https://github.com/penpot/penpot/issues/3150) - Fix error screen on upload image error [Taiga #5608](https://tree.taiga.io/project/penpot/issue/5608) - Fix bad frame-id for certain componentes [#3205](https://github.com/penpot/penpot/issues/3205) +- Fix paste elements at bottom of frame [Taig #5253](https://tree.taiga.io/project/penpot/issue/5253) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 704332767..0e49bf375 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1694,7 +1694,8 @@ [(:frame-id base) parent-id delta index]) ;; Paste inside selected frame otherwise - (let [origin-frame-id (:frame-id first-selected-obj) + (let [selected-frame-obj (get page-objects (first page-selected)) + origin-frame-id (:frame-id first-selected-obj) origin-frame-object (get page-objects origin-frame-id) margin-x (-> (- (:width origin-frame-object) (+ (:x wrapper) (:width wrapper))) @@ -1720,7 +1721,7 @@ ;; - Align it to the limits on the x and y axis ;; - Respect the distance of the object to the right and bottom in the original frame (gpt/point paste-x paste-y))] - [frame-id frame-id delta])) + [frame-id frame-id delta (dec (count (:shapes selected-frame-obj )))])) (empty? page-selected) (let [frame-id (ctst/top-nested-frame page-objects mouse-pos) From 872648d393c32481298bcdb96f05cb53b5be2867 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 14:51:10 +0200 Subject: [PATCH 18/23] :bug: Fix new-file button on project not redirecting to the new file --- CHANGES.md | 1 + frontend/src/app/main/ui/dashboard/files.cljs | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 5617a320b..f6e0c7250 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -71,6 +71,7 @@ - Fix error screen on upload image error [Taiga #5608](https://tree.taiga.io/project/penpot/issue/5608) - Fix bad frame-id for certain componentes [#3205](https://github.com/penpot/penpot/issues/3205) - Fix paste elements at bottom of frame [Taig #5253](https://tree.taiga.io/project/penpot/issue/5253) +- Fix new-file button on project not redirecting to the new file [Taiga #5610](https://tree.taiga.io/project/penpot/issue/5610) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/ui/dashboard/files.cljs b/frontend/src/app/main/ui/dashboard/files.cljs index b7d9426cf..1ad4e9ada 100644 --- a/frontend/src/app/main/ui/dashboard/files.cljs +++ b/frontend/src/app/main/ui/dashboard/files.cljs @@ -18,6 +18,7 @@ [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] + [app.util.router :as rt] [app.util.webapi :as wapi] [beicon.core :as rx] [cuerdas.core :as str] @@ -142,12 +143,22 @@ (sort-by :modified-at) (reverse))) + on-file-created + (mf/use-fn + (fn [data] + (let [pparams {:project-id (:project-id data) + :file-id (:id data)} + qparams {:page-id (get-in data [:data :pages 0])}] + (st/emit! (rt/nav :workspace pparams qparams))))) + create-file (mf/use-fn (mf/deps project) (fn [origin] - (st/emit! (with-meta (dd/create-file {:project-id (:id project)}) - {::ev/origin origin}))))] + (let [mdata {:on-success on-file-created} + params {:project-id (:id project)}] + (st/emit! (-> (dd/create-file (with-meta params mdata)) + (with-meta {::ev/origin origin}))))))] (mf/with-effect [] (let [node (mf/ref-val rowref) From fc35b0b853f5f763fd8837bc216e68b189b43185 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 15:38:54 +0200 Subject: [PATCH 19/23] :bug: Fix retrieve user comments in dashboard --- CHANGES.md | 1 + frontend/src/app/main/data/comments.cljs | 13 +++++++++++-- frontend/src/app/main/refs.cljs | 3 +++ frontend/src/app/main/ui/dashboard/comments.cljs | 2 +- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f6e0c7250..d4daeef64 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -72,6 +72,7 @@ - Fix bad frame-id for certain componentes [#3205](https://github.com/penpot/penpot/issues/3205) - Fix paste elements at bottom of frame [Taig #5253](https://tree.taiga.io/project/penpot/issue/5253) - Fix new-file button on project not redirecting to the new file [Taiga #5610](https://tree.taiga.io/project/penpot/issue/5610) +- Fix retrieve user comments in dashboard [Taiga #5607](https://tree.taiga.io/project/penpot/issue/5607) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/data/comments.cljs b/frontend/src/app/main/data/comments.cljs index 65f409178..225ddae8b 100644 --- a/frontend/src/app/main/data/comments.cljs +++ b/frontend/src/app/main/data/comments.cljs @@ -314,9 +314,18 @@ (ptk/reify ::retrieve-unread-comment-threads ptk/WatchEvent (watch [_ _ _] - (let [fetched #(assoc %2 :comment-threads (d/index-by :id %1))] + (let [fetched-comments #(assoc %2 :comment-threads (d/index-by :id %1)) + fetched-users #(assoc %2 :current-team-comments-users %1)] (->> (rp/cmd! :get-unread-comment-threads {:team-id team-id}) - (rx/map #(partial fetched %)) + (rx/merge-map + (fn [comments] + (rx/concat + (rx/of (partial fetched-comments comments)) + + (->> (rx/from (map :file-id comments)) + (rx/merge-map #(rp/cmd! :get-profiles-for-file-comments {:file-id %})) + (rx/reduce #(merge %1 (d/index-by :id %2)) {}) + (rx/map #(partial fetched-users %)))))) (rx/catch #(rx/throw {:type :comment-error}))))))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index bcb78e7a4..7f7e4bc75 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -459,6 +459,9 @@ (def current-file-comments-users (l/derived :current-file-comments-users st/state)) +(def current-team-comments-users + (l/derived :current-team-comments-users st/state)) + (def viewer-fullscreen? (l/derived (fn [state] (dm/get-in state [:viewer-local :fullscreen?])) diff --git a/frontend/src/app/main/ui/dashboard/comments.cljs b/frontend/src/app/main/ui/dashboard/comments.cljs index c03518fb9..2da270779 100644 --- a/frontend/src/app/main/ui/dashboard/comments.cljs +++ b/frontend/src/app/main/ui/dashboard/comments.cljs @@ -32,7 +32,7 @@ show-dropdown (mf/use-fn #(reset! show-dropdown? true)) hide-dropdown (mf/use-fn #(reset! show-dropdown? false)) threads-map (mf/deref refs/comment-threads) - users (mf/deref refs/current-file-comments-users) + users (mf/deref refs/current-team-comments-users) tgroups (->> (vals threads-map) (sort-by :modified-at) From 1b0a6b26ce7e574fc0108e24e0831923f30d4cc8 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 15:52:04 +0200 Subject: [PATCH 20/23] :bug: Fix problem with bool contents --- common/src/app/common/file_builder.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/app/common/file_builder.cljc b/common/src/app/common/file_builder.cljc index f8faff235..9903d0f9f 100644 --- a/common/src/app/common/file_builder.cljc +++ b/common/src/app/common/file_builder.cljc @@ -341,13 +341,15 @@ :else (let [objects (lookup-objects file) + bool-content (gsh/calc-bool-content bool objects) bool' (gsh/update-bool-selrect bool children objects)] (commit-change file {:type :mod-obj :id bool-id :operations - [{:type :set :attr :selrect :val (:selrect bool') :ignore-touched true} + [{:type :set :attr :bool-content :val bool-content :ignore-touched true} + {:type :set :attr :selrect :val (:selrect bool') :ignore-touched true} {:type :set :attr :points :val (:points bool') :ignore-touched true} {:type :set :attr :x :val (-> bool' :selrect :x) :ignore-touched true} {:type :set :attr :y :val (-> bool' :selrect :y) :ignore-touched true} From 369192a353df1f2e289cf3f28876204139ef04c3 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 11 Jul 2023 16:21:47 +0200 Subject: [PATCH 21/23] :bug: Locks shapes when moved inside a locked parent --- CHANGES.md | 1 + frontend/src/app/main/data/workspace.cljs | 14 +++++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index d4daeef64..7ee9836bd 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -73,6 +73,7 @@ - Fix paste elements at bottom of frame [Taig #5253](https://tree.taiga.io/project/penpot/issue/5253) - Fix new-file button on project not redirecting to the new file [Taiga #5610](https://tree.taiga.io/project/penpot/issue/5610) - Fix retrieve user comments in dashboard [Taiga #5607](https://tree.taiga.io/project/penpot/issue/5607) +- Locks shapes when moved inside a locked parent [Taiga #5252](https://tree.taiga.io/project/penpot/issue/5252) ### :arrow_up: Deps updates diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 0e49bf375..95050c190 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -717,17 +717,18 @@ groups-to-delete groups-to-unmask shapes-to-detach shapes-to-reroot shapes-to-deroot shapes-to-unconstraint] (let [ordered-indexes (cph/order-by-indexed-shapes objects ids) - shapes (map (d/getf objects) ordered-indexes)] + shapes (map (d/getf objects) ordered-indexes) + parent (get objects parent-id)] (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) ;; Remove layout-item properties when moving a shape outside a layout - (cond-> (not (ctl/any-layout? objects parent-id)) + (cond-> (not (ctl/any-layout? parent)) (pcb/update-shapes ordered-indexes ctl/remove-layout-item-data)) ;; Remove the hide in viewer flag - (cond-> (and (not= uuid/zero parent-id) (cph/frame-shape? objects parent-id)) + (cond-> (and (not= uuid/zero parent-id) (cph/frame-shape? parent)) (pcb/update-shapes ordered-indexes #(cond-> % (cph/frame-shape? %) (assoc :hide-in-viewer true)))) ;; Move the shapes @@ -759,8 +760,7 @@ ;; Reset constraints depending on the new parent (pcb/update-shapes shapes-to-unconstraint (fn [shape] - (let [parent (get objects parent-id) - frame-id (if (= (:type parent) :frame) + (let [frame-id (if (= (:type parent) :frame) (:id parent) (:frame-id parent)) moved-shape (assoc shape @@ -783,6 +783,10 @@ (assoc :layout-item-v-sizing :fix)) parent))) + ;; If parent locked, lock the added shapes + (cond-> (:blocked parent) + (pcb/update-shapes ordered-indexes #(assoc % :blocked true))) + ;; Resize parent containers that need to (pcb/resize-parents parents)))) From fc018b18b3f9cf384578c69aa46531d927be3745 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 12 Jul 2023 10:56:09 +0200 Subject: [PATCH 22/23] :bug: Fix rotate several elements in bulk --- CHANGES.md | 3 ++- .../app/main/data/workspace/modifiers.cljs | 26 +++++++++++++++++++ .../app/main/data/workspace/transforms.cljs | 8 +++--- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 7ee9836bd..7d22d1195 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -74,7 +74,8 @@ - Fix new-file button on project not redirecting to the new file [Taiga #5610](https://tree.taiga.io/project/penpot/issue/5610) - Fix retrieve user comments in dashboard [Taiga #5607](https://tree.taiga.io/project/penpot/issue/5607) - Locks shapes when moved inside a locked parent [Taiga #5252](https://tree.taiga.io/project/penpot/issue/5252) - +- Fix rotate several elements in bulk [Taiga #5165](https://tree.taiga.io/project/penpot/issue/5165) + ### :arrow_up: Deps updates - Update google fonts catalog (at 2023/07/06) [Taiga #5592](https://tree.taiga.io/project/penpot/issue/5592) diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index a27f9c4d1..8f86d72b7 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -371,6 +371,32 @@ (assoc state :workspace-modifiers modif-tree)))))) +;; This function is similar to set-rotation-modifiers but: +;; - It consideres the center for everyshape instead of the center of the total selrect +;; - The angle param is the desired final value, not a delta +(defn set-delta-rotation-modifiers + ([angle shapes] + (ptk/reify ::set-delta-rotation-modifiers + ptk/UpdateEvent + (update [_ state] + (let [objects (wsh/lookup-page-objects state) + ids + (->> shapes + (remove #(get % :blocked false)) + (filter #((cpc/editable-attrs (:type %)) :rotation)) + (map :id)) + + get-modifier + (fn [shape] + (let [delta (- angle (:rotation shape)) + center (gsh/center-shape shape)] + (ctm/rotation-modifiers shape center delta))) + + modif-tree + (-> (build-modif-tree ids objects get-modifier) + (gsh/set-objects-modifiers objects))] + + (assoc state :workspace-modifiers modif-tree)))))) (defn apply-modifiers ([] diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 862cb4599..ad43c6bfc 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -343,12 +343,10 @@ (let [page-id (:current-page-id state) objects (wsh/lookup-page-objects state page-id) - rotate-shape (fn [shape] - (let [delta (- rotation (:rotation shape))] - (dwm/set-rotation-modifiers delta [shape])))] + shapes (->> ids (map #(get objects %)))] (rx/concat - (rx/from (->> ids (map #(get objects %)) (map rotate-shape))) - (rx/of (dwm/apply-modifiers))))))) + (rx/of (dwm/set-delta-rotation-modifiers rotation shapes)) + (rx/of (dwm/apply-modifiers))))))) ;; -- Move ---------------------------------------------------------- From 119b3a405ca9c8e80ef21a596a0f32b315d0fa76 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Wed, 12 Jul 2023 16:55:51 +0200 Subject: [PATCH 23/23] :bug: Fix duplicate page with comnponents duplicates the components --- frontend/src/app/main/data/workspace.cljs | 46 +++++++++++++++++++---- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 95050c190..95a975fb3 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -21,6 +21,7 @@ [app.common.text :as txt] [app.common.transit :as t] [app.common.types.component :as ctk] + [app.common.types.components-list :as ctkl] [app.common.types.container :as ctn] [app.common.types.file :as ctf] [app.common.types.pages-list :as ctpl] @@ -448,18 +449,49 @@ (ptk/reify ::duplicate-page ptk/WatchEvent (watch [it state _] - (let [id (uuid/next) - pages (get-in state [:workspace-data :pages-index]) - unames (cp/retrieve-used-names pages) - page (get-in state [:workspace-data :pages-index page-id]) - name (cp/generate-unique-name unames (:name page)) + (let [id (uuid/next) + pages (get-in state [:workspace-data :pages-index]) + unames (cp/retrieve-used-names pages) + page (get-in state [:workspace-data :pages-index page-id]) + name (cp/generate-unique-name unames (:name page)) + fdata (:workspace-data state) + components-v2 (dm/get-in fdata [:options :components-v2]) + objects (->> (:objects page) + (d/mapm (fn [_ val] (dissoc val :use-for-thumbnail?)))) + main-instances-ids (set (keep #(when (ctk/main-instance? (val %)) (key %)) objects)) + ids-to-remove (set (apply concat (map #(cph/get-children-ids objects %) main-instances-ids))) + + add-component-copy + (fn [objs id shape] + (let [component (ctkl/get-component fdata (:component-id shape)) + [new-shape new-shapes] + (ctn/make-component-instance page + component + fdata + (gpt/point (:x shape) (:y shape)) + components-v2 + {:keep-ids? true}) + children (into {} (map (fn [shape] [(:id shape) shape]) new-shapes)) + objs (assoc objs id new-shape)] + (merge objs children))) + + objects + (reduce + (fn [objs [id shape]] + (cond (contains? main-instances-ids id) + (add-component-copy objs id shape) + (contains? ids-to-remove id) + objs + :else + (assoc objs id shape))) + {} + objects) page (-> page (assoc :name name) (assoc :id id) (assoc :objects - (->> (:objects page) - (d/mapm (fn [_ val] (dissoc val :use-for-thumbnail?)))))) + objects)) changes (-> (pcb/empty-changes it) (pcb/add-page id page))]