Merge remote-tracking branch 'origin/staging' into develop

This commit is contained in:
Alejandro Alonso 2023-03-24 12:33:14 +01:00
commit 4fad2ab619
59 changed files with 608 additions and 325 deletions

View file

@ -279,15 +279,19 @@
(->
(assoc-in (conj path :position) (:position comment-thread))
(assoc-in (conj path :frame-id) (:frame-id comment-thread))))))
(fetched [data state]
(let [state (assoc state :comment-threads (d/index-by :id data))]
(reduce set-comment-threds state data)))]
(fetched [[users comments] state]
(let [state (-> state
(assoc :comment-threads (d/index-by :id comments))
(assoc :current-file-comments-users (d/index-by :id users)))]
(reduce set-comment-threds state comments)))]
(ptk/reify ::retrieve-comment-threads
ptk/WatchEvent
(watch [_ state _]
(let [share-id (-> state :viewer-local :share-id)]
(->> (rp/cmd! :get-comment-threads {:file-id file-id :share-id share-id})
(->> (rx/zip (rp/cmd! :get-team-users {:file-id file-id})
(rp/cmd! :get-comment-threads {:file-id file-id :share-id share-id}))
(rx/take 1)
(rx/map #(partial fetched %))
(rx/catch #(rx/throw {:type :comment-error}))))))))

View file

@ -7,7 +7,7 @@
(ns app.main.data.shortcuts
(:refer-clojure :exclude [meta reset!])
(:require
["mousetrap" :as mousetrap]
["./shortcuts_impl.js$default" :as mousetrap]
[app.common.logging :as log]
[app.common.spec :as us]
[app.config :as cf]
@ -32,6 +32,7 @@
(def up-arrow "\u2191")
(def right-arrow "\u2192")
(def down-arrow "\u2193")
(def tab "tab")
(defn c-mod
"Adds the control/command modifier to a shortcuts depending on the

View file

@ -0,0 +1,27 @@
/**
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright (c) KALEIDOS INC
*/
"use strict";
import Mousetrap from 'mousetrap'
const target = Mousetrap.prototype || Mousetrap;
target.stopCallback = function(e, element, combo) {
// if the element has the class "mousetrap" then no need to stop
if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) {
return false;
}
// stop for input, select, textarea and button
return element.tagName == 'INPUT' ||
element.tagName == 'SELECT' ||
element.tagName == 'TEXTAREA' ||
element.tagName == 'BUTTON' ||
(element.contentEditable && element.contentEditable == 'true');
}
export default Mousetrap;

View file

@ -37,7 +37,7 @@
(s/def ::created-at ::us/inst)
(s/def ::password-1 ::us/string)
(s/def ::password-2 ::us/string)
(s/def ::password-old ::us/string)
(s/def ::password-old (s/nilable ::us/string))
(s/def ::profile
(s/keys :req-un [::id]

View file

@ -300,6 +300,13 @@
(update [_ state]
(update-in state [:viewer-local :fullscreen?] not))))
(defn exit-fullscreen
[]
(ptk/reify ::exit-fullscreen
ptk/UpdateEvent
(update [_ state]
(assoc-in state [:viewer-local :fullscreen?] false))))
(defn set-viewport-size
[{:keys [size]}]
(ptk/reify ::set-viewport-size

View file

@ -2043,6 +2043,8 @@
(dm/export dws/select-all)
(dm/export dws/select-inside-group)
(dm/export dws/select-shape)
(dm/export dws/select-prev-shape)
(dm/export dws/select-next-shape)
(dm/export dws/shift-select-shapes)
;; Highlight

View file

@ -8,6 +8,7 @@
(:require
[app.common.colors :as colors]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.pages.helpers :as cph]
[app.main.broadcast :as mbc]
[app.main.data.modal :as md]
@ -81,6 +82,8 @@
text-ids (filter is-text? ids)
shape-ids (remove is-text? ids)
undo-id (js/Symbol)
attrs
(cond-> {}
(contains? color :color)
@ -104,8 +107,10 @@
transform-attrs #(transform % attrs)]
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(rx/from (map #(dwt/update-text-with-function % transform-attrs) text-ids))
(rx/of (dch/update-shapes shape-ids transform-attrs)))))
(rx/of (dch/update-shapes shape-ids transform-attrs))
(rx/of (dwu/commit-undo-transaction undo-id)))))
(defn swap-attrs [shape attr index new-index]
(let [first (get-in shape [attr index])
@ -339,7 +344,8 @@
(defn change-text-color
[old-color new-color index node]
(let [fills (map #(dissoc % :fill-color-ref-id :fill-color-ref-file) (:fills node))
parsed-color (d/without-nils (color-att->text old-color))
parsed-color (-> (d/without-nils (color-att->text old-color))
(dissoc :fill-color-ref-id :fill-color-ref-file))
parsed-new-color (d/without-nils (color-att->text new-color))
has-color? (d/index-of fills parsed-color)]
(cond-> node
@ -365,23 +371,33 @@
(rx/of (dwu/commit-undo-transaction undo-id)))))))
(defn apply-color-from-palette
[color is-alt?]
[color stroke?]
(ptk/reify ::apply-color-from-palette
ptk/WatchEvent
(watch [_ state _]
(let [objects (wsh/lookup-page-objects state)
selected (->> (wsh/lookup-selected state)
(cph/clean-loops objects))
selected-obj (keep (d/getf objects) selected)
select-shapes-for-color (fn [shape objects]
(let [shapes (case (:type shape)
:group (cph/get-children objects (:id shape))
[shape])]
(->> shapes
(remove cph/group-shape?)
(map :id))))
ids (mapcat #(select-shapes-for-color % objects) selected-obj)]
(if is-alt?
ids
(loop [pending (seq selected)
result []]
(if (empty? pending)
result
(let [cur (first pending)
;; We treat frames with no fill the same as groups
group? (or (cph/group-shape? objects cur)
(and (cph/frame-shape? objects cur)
(empty? (dm/get-in objects [cur :fills]))))
pending
(if group?
(concat pending (dm/get-in objects [cur :shapes]))
pending)
result (cond-> result (not group?) (conj cur))]
(recur (rest pending) result))))]
(if stroke?
(rx/of (change-stroke ids (merge uc/empty-color color) 0))
(rx/of (change-fill ids (merge uc/empty-color color) 0)))))))

View file

@ -7,6 +7,7 @@
(ns app.main.data.workspace.groups
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.geom.shapes :as gsh]
[app.common.pages.changes-builder :as pcb]
[app.common.pages.helpers :as cph]
@ -195,6 +196,11 @@
(keep :id))
selected)
child-ids
(into (d/ordered-set)
(mapcat #(dm/get-in objects [% :shapes]))
selected)
changes {:redo-changes (vec (mapcat :redo-changes changes-list))
:undo-changes (vec (mapcat :undo-changes changes-list))
:origin it}
@ -203,7 +209,8 @@
(rx/of (dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(ptk/data-event :layout/update parents)
(dwu/commit-undo-transaction undo-id))))))
(dwu/commit-undo-transaction undo-id)
(dws/select-shapes child-ids))))))
(def mask-group
(ptk/reify ::mask-group

View file

@ -112,7 +112,7 @@
(watch [_ state _]
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
frame (cph/get-frame objects shape)
frame (cph/get-root-frame objects (:id shape))
flows (get-in state [:workspace-data
:pages-index
page-id

View file

@ -176,10 +176,11 @@
(ptk/reify ::rename-color
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
object (get-in data [:colors id])
new-object (assoc object :name new-name)]
(do-update-color it state new-object file-id)))))
(when (and (some? new-name) (not= "" new-name))
(let [data (get state :workspace-data)
object (get-in data [:colors id])
new-object (assoc object :name new-name)]
(do-update-color it state new-object file-id))))))
(defn delete-color
[{:keys [id] :as params}]
@ -211,14 +212,15 @@
(ptk/reify ::rename-media
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
object (get-in data [:media id])
new-object (assoc object :path path :name name)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/update-media new-object))]
(rx/of (dch/commit-changes changes))))))
(when (and (some? new-name) (not= "" new-name))
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
object (get-in data [:media id])
new-object (assoc object :path path :name name)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/update-media new-object))]
(rx/of (dch/commit-changes changes)))))))
(defn delete-media
@ -281,11 +283,12 @@
(ptk/reify ::rename-typography
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
object (get-in data [:typographies id])
new-object (assoc object :path path :name name)]
(do-update-tipography it state new-object file-id)))))
(when (and (some? new-name) (not= "" new-name))
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
object (get-in data [:typographies id])
new-object (assoc object :path path :name name)]
(do-update-tipography it state new-object file-id))))))
(defn delete-typography
[id]
@ -342,27 +345,28 @@
(ptk/reify ::rename-component
ptk/WatchEvent
(watch [it state _]
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
(when (and (some? new-name) (not= "" new-name))
(let [data (get state :workspace-data)
[path name] (cph/parse-path-name new-name)
update-fn
(fn [component]
;; NOTE: we need to ensure the component exists,
;; because there are small possibilities of race
;; conditions with component deletion.
(when component
(-> component
(assoc :path path)
(assoc :name name)
(update :objects
;; Give the same name to the root shape
#(assoc-in % [id :name] name)))))
update-fn
(fn [component]
;; NOTE: we need to ensure the component exists,
;; because there are small possibilities of race
;; conditions with component deletion.
(when component
(-> component
(assoc :path path)
(assoc :name name)
(update :objects
;; Give the same name to the root shape
#(assoc-in % [id :name] name)))))
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/update-component id update-fn))]
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/update-component id update-fn))]
(rx/of (dch/commit-changes changes))))))
(rx/of (dch/commit-changes changes)))))))
(defn duplicate-component
"Create a new component copied from the one with the given id."

View file

@ -131,6 +131,55 @@
objects (wsh/lookup-page-objects state page-id)]
(rx/of (dwc/expand-all-parents [id] objects)))))))
(defn select-prev-shape
([]
(ptk/reify ::select-prev-shape
ptk/WatchEvent
(watch [_ state _]
(let [selected (wsh/lookup-selected state)
count-selected (count selected)
first-selected (first selected)
page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
current (get objects first-selected)
parent (get objects (:parent-id current))
sibling-ids (:shapes parent)
current-index (d/index-of sibling-ids first-selected)
sibling (if (= (dec (count sibling-ids)) current-index)
(first sibling-ids)
(nth sibling-ids (inc current-index)))]
(cond
(= 1 count-selected)
(rx/of (select-shape sibling))
(> count-selected 1)
(rx/of (select-shape first-selected))))))))
(defn select-next-shape
([]
(ptk/reify ::select-next-shape
ptk/WatchEvent
(watch [_ state _]
(let [selected (wsh/lookup-selected state)
count-selected (count selected)
first-selected (first selected)
page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
current (get objects first-selected)
parent (get objects (:parent-id current))
sibling-ids (:shapes parent)
current-index (d/index-of sibling-ids first-selected)
sibling (if (= 0 current-index)
(last sibling-ids)
(nth sibling-ids (dec current-index)))]
(cond
(= 1 count-selected)
(rx/of (select-shape sibling))
(> count-selected 1)
(rx/of (select-shape first-selected))))))))
(defn deselect-shape
[id]
(us/verify ::us/uuid id)
@ -140,13 +189,14 @@
(update-in state [:workspace-local :selected] disj id))))
(defn shift-select-shapes
([id] (shift-select-shapes id nil))
([id]
(shift-select-shapes id nil))
([id objects]
(ptk/reify ::shift-select-shapes
ptk/UpdateEvent
(update [_ state]
(let [page-id (:current-page-id state)
objects (or objects (wsh/lookup-page-objects state page-id))
(let [objects (or objects (wsh/lookup-page-objects state))
selection (-> state
wsh/lookup-selected
(conj id))]

View file

@ -67,8 +67,8 @@
:cut {:tooltip (ds/meta "X")
:command (ds/c-mod "x")
:subsections [:edit]
:fn #(emit-when-no-readonly (dw/copy-selected)
(dw/delete-selected))}
:fn #(emit-when-no-readonly (dw/copy-selected)
(dw/delete-selected))}
:paste {:tooltip (ds/meta "V")
:disabled true
@ -110,7 +110,7 @@
;; MODIFY LAYERS
:group {:tooltip (ds/meta "G")
:command (ds/c-mod "g")
@ -222,7 +222,7 @@
:fn #(emit-when-no-readonly (dwsl/toggle-layout-flex))}
;; TOOLS
:draw-frame {:tooltip "B"
:command ["b" "a"]
:subsections [:tools :basics]
@ -247,7 +247,7 @@
:command "t"
:subsections [:tools]
:fn #(emit-when-no-readonly dwtxt/start-edit-if-selected
(dwd/select-for-drawing :text))}
(dwd/select-for-drawing :text))}
:draw-path {:tooltip "P"
:command "p"
@ -300,7 +300,7 @@
:fn #(emit-when-no-readonly (dw/toggle-focus-mode))}
;; ITEM ALIGNMENT
:align-left {:tooltip (ds/alt "A")
:command "alt+a"
:subsections [:alignment]
@ -342,7 +342,7 @@
:fn #(emit-when-no-readonly (dw/distribute-objects :vertical))}
;; MAIN MENU
:toggle-rules {:tooltip (ds/meta-shift "R")
:command (ds/c-mod "shift+r")
:subsections [:main-menu]
@ -387,7 +387,7 @@
:command (ds/c-mod "shift+e")
:subsections [:basics :main-menu]
:fn #(st/emit!
(de/show-workspace-export-dialog))}
(de/show-workspace-export-dialog))}
:toggle-snap-guide {:tooltip (ds/meta-shift "G")
:command (ds/c-mod "shift+g")
@ -400,7 +400,7 @@
:fn #(st/emit! (toggle-layout-flag :shortcuts))}
;; PANELS
:toggle-layers {:tooltip (ds/alt "L")
:command (ds/a-mod "l")
:subsections [:panels]
@ -420,15 +420,15 @@
:command (ds/a-mod "p")
:subsections [:panels]
:fn #(do (r/set-resize-type! :bottom)
(emit-when-no-readonly (dw/remove-layout-flag :textpalette)
(toggle-layout-flag :colorpalette)))}
(emit-when-no-readonly (dw/remove-layout-flag :textpalette)
(toggle-layout-flag :colorpalette)))}
:toggle-textpalette {:tooltip (ds/alt "T")
:command (ds/a-mod "t")
:subsections [:panels]
:fn #(do (r/set-resize-type! :bottom)
(emit-when-no-readonly (dw/remove-layout-flag :colorpalette)
(toggle-layout-flag :textpalette)))}
(emit-when-no-readonly (dw/remove-layout-flag :colorpalette)
(toggle-layout-flag :textpalette)))}
:hide-ui {:tooltip "\\"
:command "\\"
@ -436,7 +436,7 @@
:fn #(st/emit! (toggle-layout-flag :hide-ui))}
;; ZOOM-WORKSPACE
:increase-zoom {:tooltip "+"
:command ["+" "="]
:subsections [:zoom-workspace]
@ -473,7 +473,7 @@
:fn identity}
;; NAVIGATION
:open-viewer {:tooltip "G V"
:command "g v"
@ -495,7 +495,18 @@
:subsections [:navigation-workspace]
:fn #(st/emit! (dw/go-to-dashboard))}
:select-prev {:tooltip (ds/shift "tab")
:command "shift+tab"
:subsections [:navigation-workspace]
:fn #(st/emit! (dw/select-prev-shape))}
:select-next {:tooltip ds/tab
:command "tab"
:subsections [:navigation-workspace]
:fn #(st/emit! (dw/select-next-shape))}
;; SHAPE
:bool-union {:tooltip (ds/meta (ds/alt "U"))
:command (ds/c-mod "alt+u")

View file

@ -202,22 +202,22 @@
(st/emit! (dwu/commit-undo-transaction undo-id)))))
(def shortcuts
{:align-left {:tooltip (ds/meta (ds/alt "l"))
:command (ds/c-mod "alt+l")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "left"})}
:align-right {:tooltip (ds/meta (ds/alt "r"))
:command (ds/c-mod "alt+r")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "right"})}
:align-center {:tooltip (ds/meta (ds/alt "t"))
:command (ds/c-mod "alt+t")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "center"})}
:align-justify {:tooltip (ds/meta (ds/alt "j"))
:command (ds/c-mod "alt+j")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "justify"})}
{:text-align-left {:tooltip (ds/meta (ds/alt "l"))
:command (ds/c-mod "alt+l")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "left"})}
:text-align-right {:tooltip (ds/meta (ds/alt "r"))
:command (ds/c-mod "alt+r")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "right"})}
:text-align-center {:tooltip (ds/meta (ds/alt "t"))
:command (ds/c-mod "alt+t")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "center"})}
:text-align-justify {:tooltip (ds/meta (ds/alt "j"))
:command (ds/c-mod "alt+j")
:subsections [:text-editor]
:fn #(update-attrs-when-no-readonly {:text-align "justify"})}
:underline {:tooltip (ds/meta "u")
:command (ds/c-mod "u")

View file

@ -251,7 +251,7 @@
(into [] (distinct) (conj coll item)))
(mf/defc multi-input
[{:keys [form label class name trim valid-item-fn on-submit] :as props}]
[{:keys [form label class name trim valid-item-fn caution-item-fn on-submit] :as props}]
(let [form (or form (mf/use-ctx form-ctx))
input-name (get props :name)
touched? (get-in @form [:touched input-name])
@ -309,7 +309,9 @@
(on-submit form))
(when (not (str/empty? @value))
(reset! value "")
(swap! items conj-dedup {:text val :valid (valid-item-fn val)}))))
(swap! items conj-dedup {:text val
:valid (valid-item-fn val)
:caution (caution-item-fn val)}))))
(and (kbd/backspace? event)
(str/empty? @value))
@ -361,6 +363,7 @@
[:div.selected-item {:key (:text item)
:tab-index "0"
:on-key-down (partial manage-key-down item)}
[:span.around {:class (when-not (:valid item) "invalid")}
[:span.around {:class (dom/classnames "invalid" (not (:valid item))
"caution" (:caution item))}
[:span.text (:text item)]
[:span.icon {:on-click #(remove-item! item)} i/cross]]])])]))

View file

@ -59,7 +59,8 @@
(assert (fn? on-edit) "missing `on-edit` prop")
(assert (fn? on-menu-close) "missing `on-menu-close` prop")
(assert (boolean? navigate?) "missing `navigate?` prop")
(let [is-lib-page? (= :libraries origin)
(let [is-lib-page? (= :libraries origin)
is-search-page? (= :search origin)
top (or top 0)
left (or left 0)
@ -264,28 +265,30 @@
[{:option-name (tr "dashboard.open-in-new-tab")
:id "file-open-new-tab"
:option-handler on-new-tab}
{:option-name (tr "labels.rename")
:id "file-rename"
:option-handler on-edit
:data-test "file-rename"}
{:option-name (tr "dashboard.duplicate")
:id "file-duplicate"
:option-handler on-duplicate
:data-test "file-duplicate"}
(when (and (not is-lib-page?) (or (seq current-projects) (seq other-teams)))
(when (not is-search-page?)
{:option-name (tr "labels.rename")
:id "file-rename"
:option-handler on-edit
:data-test "file-rename"}
{:option-name (tr "dashboard.duplicate")
:id "file-duplicate"
:option-handler on-duplicate
:data-test "file-duplicate"})
(when (and (not is-lib-page?) (not is-search-page?) (or (seq current-projects) (seq other-teams)))
{:option-name (tr "dashboard.move-to")
:id "file-move-to"
:sub-options sub-options
:data-test "file-move-to"})
(if (:is-shared file)
{:option-name (tr "dashboard.unpublish-shared")
:id "file-del-shared"
:option-handler on-del-shared
:data-test "file-del-shared"}
{:option-name (tr "dashboard.add-shared")
:id "file-add-shared"
:option-handler on-add-shared
:data-test "file-add-shared"})
(when (not is-search-page?)
(if (:is-shared file)
{:option-name (tr "dashboard.unpublish-shared")
:id "file-del-shared"
:option-handler on-del-shared
:data-test "file-del-shared"}
{:option-name (tr "dashboard.add-shared")
:id "file-add-shared"
:option-handler on-add-shared
:data-test "file-add-shared"}))
{:option-name :separator}
{:option-name (tr "dashboard.download-binary-file")
:id "file-download-binary"
@ -295,7 +298,7 @@
:id "file-download-standard"
:option-handler on-export-standard-files
:data-test "download-standard-file"}
(when (not is-lib-page?)
(when (and (not is-lib-page?) (not is-search-page?))
{:option-name :separator}
{:option-name (tr "labels.delete")
:id "file-delete"

View file

@ -87,4 +87,5 @@
:else
[:& grid {:files result
:hide-new? true
:origin :search
:limit limit}])]]))

View file

@ -39,7 +39,9 @@
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})))
#(st/emit! (modal/show {:type :invite-members
:team team
:origin :team})))
members-section? (= section :dashboard-team-members)
settings-section? (= section :dashboard-team-settings)
@ -98,7 +100,10 @@
{::mf/register modal/components
::mf/register-as :invite-members}
[{:keys [team origin]}]
(let [perms (:permissions team)
(let [members-map (mf/deref refs/dashboard-team-members)
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
@ -111,6 +116,9 @@
(modal/hide)
(dd/fetch-team-invitations)))
current-data-emails (into #{} (dm/get-in @form [:clean-data :emails]))
current-members-emails (into #{} (map (comp :email second)) members-map)
on-error
(fn [{:keys [type code] :as error}]
(cond
@ -148,17 +156,23 @@
[:div.error
[:span.icon i/msg-error]
[:span.text @error-text]])
(when (some current-data-emails current-members-emails)
[:div.warning
[:span.icon i/msg-warning]
[:span.text (tr "modals.invite-member.repeated-invitation")]])
[:div.form-row
[:p.label (tr "onboarding.choice.team-up.roles")]
[:& fm/select {:name :role :options roles}]]
[:div.form-row
[:& fm/multi-input {:type "email"
:name :emails
:auto-focus? true
:trim true
:valid-item-fn us/parse-email
:caution-item-fn current-members-emails
:label (tr "modals.invite-member.emails")
:on-submit on-submit}]]

View file

@ -307,7 +307,7 @@
on-pointer-down
(mf/use-callback
(mf/deps frame-id padding-num)
(mf/deps frame-id rect-data padding-num)
(fn [event]
(dom/capture-pointer event)
(reset! resizing? true)
@ -382,9 +382,9 @@
pill-width (/ flex-display-pill-width zoom)
pill-height (/ flex-display-pill-height zoom)
hover? #(or hover-all?
(and (or (= % :p1) (= % :p3)) hover-v?)
(and (or (= % :p2) (= % :p4)) hover-h?)
(= @hover %))
(and (or (= % :p1) (= % :p3)) hover-v?)
(and (or (= % :p2) (= % :p4)) hover-h?)
(= @hover %))
negate {:p1 (if (:flip-y frame) true false)
:p2 (if (:flip-x frame) true false)
:p3 (if (:flip-y frame) true false)
@ -854,9 +854,9 @@
(mf/defc padding
[{:keys [frame zoom alt? shift?]}]
(when frame
[:g.measurement-gaps {:pointer-events "none"}
[:g.hover-shapes
[:& padding-rects {:frame frame :zoom zoom :alt? alt? :shift? shift?}]]]))
[:g.measurement-gaps {:pointer-events "none"}
[:g.hover-shapes
[:& padding-rects {:frame frame :zoom zoom :alt? alt? :shift? shift?}]]]))
(mf/defc gap
[{:keys [frame zoom]}]

View file

@ -32,7 +32,10 @@
(defn- on-success
[form]
(reset! form nil)
(let [msg (tr "dashboard.notifications.password-saved")]
(let [password-old-node (dom/get-element "password-old")
msg (tr "dashboard.notifications.password-saved")]
(dom/clean-value! password-old-node)
(dom/focus! password-old-node)
(st/emit! (dm/success msg))))
(defn- on-submit
@ -45,7 +48,7 @@
(s/def ::password-1 ::us/not-empty-string)
(s/def ::password-2 ::us/not-empty-string)
(s/def ::password-old ::us/not-empty-string)
(s/def ::password-old (s/nilable ::us/string))
(defn- password-equality
[errors data]
@ -66,9 +69,10 @@
(mf/defc password-form
[{:keys [locale] :as props}]
(let [form (fm/use-form :spec ::password-form
:validators [password-equality]
:initial {})]
(let [initial (mf/use-memo (constantly {:password-old nil}))
form (fm/use-form :spec ::password-form
:validators [password-equality]
:initial initial)]
[:& fm/form {:class "password-form"
:on-submit on-submit
:form form}
@ -77,6 +81,7 @@
[:& fm/input
{:type "password"
:name :password-old
:auto-focus? true
:label (t locale "labels.old-password")}]]
[:div.fields-row

View file

@ -5,6 +5,7 @@
;; Copyright (c) KALEIDOS INC
(ns app.main.ui.viewer
(:import goog.events.EventType)
(:require
[app.common.data :as d]
[app.common.data.macros :as dm]
@ -33,6 +34,7 @@
[app.main.ui.viewer.thumbnails :refer [thumbnails-panel]]
[app.util.dom :as dom]
[app.util.dom.normalize-wheel :as nw]
[app.util.globals :as globals]
[app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[app.util.webapi :as wapi]
@ -328,7 +330,13 @@
(dom/stop-propagation event)
(if shift?
(dom/set-h-scroll-pos! viewer-section new-scroll-pos)
(dom/set-scroll-pos! viewer-section new-scroll-pos)))))))]
(dom/set-scroll-pos! viewer-section new-scroll-pos)))))))
on-exit-fullscreen
(mf/use-callback
(fn []
(when (not (dom/fullscreen?))
(st/emit! (dv/exit-fullscreen)))))]
(hooks/use-shortcuts ::viewer sc/shortcuts)
(when (nil? page)
@ -346,11 +354,20 @@
(dom/set-html-title (str "\u25b6 " (tr "title.viewer" name))))))
(mf/with-effect []
(let [key1 (events/listen js/window "click" on-click)
key2 (events/listen (mf/ref-val viewer-section-ref) "wheel" on-wheel #js {"passive" false})]
(dom/set-html-theme-color clr/gray-50 "dark")
(let [events
[(events/listen globals/window EventType.CLICK on-click)
(events/listen (mf/ref-val viewer-section-ref) EventType.WHEEL on-wheel #js {"passive" false})]]
(doseq [event dom/fullscreen-events]
(.addEventListener globals/document event on-exit-fullscreen false))
(fn []
(events/unlistenByKey key1)
(events/unlistenByKey key2))))
(doseq [key events]
(events/unlistenByKey key))
(doseq [event dom/fullscreen-events]
(.removeEventListener globals/document event on-exit-fullscreen)))))
(mf/use-effect
(fn []

View file

@ -234,10 +234,11 @@
;; When we have a text with grow-type :auto-height or :auto-height we need to check the correct height
;; otherwise the center alignment will break
tr-shape (when text-modifier (dwt/apply-text-modifier shape text-modifier))
shape (cond-> shape
(and (some? text-modifier) (#{:auto-height :auto-width} (:grow-type shape)))
(assoc :width (:width tr-shape) :height (:height tr-shape)))
shape
(if (some? text-modifier)
(let [{:keys [width height]} (dwt/apply-text-modifier shape text-modifier)]
(assoc shape :width width :height height))
shape)
shape (hooks/use-equal-memo shape)

View file

@ -1173,29 +1173,13 @@
(:color color) (:color color)
:else (:value color))
;; TODO: looks like the first argument is not necessary
;; TODO: this code should be out of this UI component
apply-color
(fn [_ event]
(let [objects (wsh/lookup-page-objects @st/state)
selected (->> (wsh/lookup-selected @st/state)
(cph/clean-loops objects))
selected-obj (keep (d/getf objects) selected)
select-shapes-for-color (fn [shape objects]
(let [shapes (case (:type shape)
:group (cph/get-children objects (:id shape))
[shape])]
(->> shapes
(remove cph/group-shape?)
(map :id))))
ids (mapcat #(select-shapes-for-color % objects) selected-obj)]
(if (kbd/alt? event)
(st/emit! (dc/change-stroke ids (merge uc/empty-color color) 0))
(st/emit! (dc/change-fill ids (merge uc/empty-color color) 0)))))
(fn [event]
(st/emit! (dc/apply-color-from-palette (merge uc/empty-color color) (kbd/alt? event))))
rename-color
(fn [name]
(st/emit! (dwl/update-color (assoc color :name name) file-id)))
(st/emit! (dwl/rename-color file-id (:id color) name)))
edit-color
(fn [new-color]
@ -1300,8 +1284,7 @@
:selected (contains? selected-colors (:id color)))
:on-context-menu on-context-menu
:on-click (when-not (:editing @state)
#(on-asset-click % (:id color)
(partial apply-color (:id color))))
#(on-asset-click % (:id color) apply-color))
:ref item-ref
:draggable (and (not workspace-read-only?) (not (:editing @state)))
:on-drag-start on-color-drag-start

View file

@ -11,6 +11,7 @@
[app.common.uuid :as uuid]
[app.main.data.workspace.changes :as dch]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.shortcuts :as sc]
[app.main.data.workspace.texts :as dwt]
[app.main.data.workspace.undo :as dwu]
[app.main.fonts :as fonts]
@ -25,8 +26,6 @@
[cuerdas.core :as str]
[rumext.v2 :as mf]))
(mf/defc text-align-options
[{:keys [values on-change on-blur] :as props}]
(let [{:keys [text-align]} values
@ -38,22 +37,22 @@
;; --- Align
[:div.align-icons
[:span.tooltip.tooltip-bottom
{:alt (tr "workspace.options.text-options.align-left")
{:alt (tr "workspace.options.text-options.align-left" (sc/get-tooltip :text-align-left))
:class (dom/classnames :current (= "left" text-align))
:on-click #(handle-change % "left")}
i/text-align-left]
[:span.tooltip.tooltip-bottom
{:alt (tr "workspace.options.text-options.align-center")
{:alt (tr "workspace.options.text-options.align-center" (sc/get-tooltip :text-align-center))
:class (dom/classnames :current (= "center" text-align))
:on-click #(handle-change % "center")}
i/text-align-center]
[:span.tooltip.tooltip-bottom
{:alt (tr "workspace.options.text-options.align-right")
{:alt (tr "workspace.options.text-options.align-right" (sc/get-tooltip :text-align-right))
:class (dom/classnames :current (= "right" text-align))
:on-click #(handle-change % "right")}
i/text-align-right]
[:span.tooltip.tooltip-bottom
{:alt (tr "workspace.options.text-options.align-justify")
{:alt (tr "workspace.options.text-options.align-justify" (sc/get-tooltip :text-align-justify))
:class (dom/classnames :current (= "justify" text-align))
:on-click #(handle-change % "justify")}
i/text-align-justify]]))
@ -149,13 +148,13 @@
i/minus]
[:span.tooltip.tooltip-bottom
{:alt (tr "workspace.options.text-options.underline")
{:alt (tr "workspace.options.text-options.underline" (sc/get-tooltip :underline))
:class (dom/classnames :current (= "underline" text-decoration))
:on-click #(handle-change % "underline")}
i/underline]
[:span.tooltip.tooltip-bottom
{:alt (tr "workspace.options.text-options.strikethrough")
{:alt (tr "workspace.options.text-options.strikethrough" (sc/get-tooltip :line-through))
:class (dom/classnames :current (= "line-through" text-decoration))
:on-click #(handle-change % "line-through")}
i/strikethrough]]))

View file

@ -143,6 +143,8 @@
;; shortcuts.open-color-picker
;; shortcuts.open-comments
;; shortcuts.open-dashboard
;; shortcuts.select-prev
;; shortcuts.select-next
;; shortcuts.open-inspect
;; shortcuts.open-interactions
;; shortcuts.open-viewer

View file

@ -134,12 +134,14 @@
;; STREAMS
move-stream (mf/use-memo #(rx/subject))
frame-parent (mf/use-memo
guide-frame (mf/use-memo
(mf/deps @hover-ids base-objects)
(fn []
(let [parent (get base-objects (last @hover-ids))]
(when (= :frame (:type parent))
parent))))
(let [parent-id
(->> @hover-ids
(d/seek (partial cph/root-frame? base-objects)))]
(when (some? parent-id)
(get base-objects parent-id)))))
zoom (d/check-num zoom 1)
drawing-tool (:tool drawing)
@ -495,7 +497,7 @@
[:& guides/viewport-guides
{:zoom zoom
:vbox vbox
:hover-frame frame-parent
:hover-frame guide-frame
:disabled-guides? disabled-guides?
:modifiers modifiers}])

View file

@ -434,7 +434,15 @@
(:id component)
(gpt/point final-x final-y))))
;; Will trigger when the user drags an image from a browser to the viewport
;; Will trigger when the user drags an image from a browser to the viewport (firefox and chrome do it a bit different depending on the origin)
(dnd/has-type? event "Files")
(let [files (dnd/get-files event)
params {:file-id (:id file)
:position viewport-coord
:blobs (seq files)}]
(st/emit! (dwm/upload-media-workspace params)))
;; Will trigger when the user drags an image from a browser to the viewport (firefox and chrome do it a bit different depending on the origin)
(dnd/has-type? event "text/uri-list")
(let [data (dnd/get-data event "text/uri-list")
lines (str/lines data)

View file

@ -58,7 +58,8 @@
shape (or drawing-obj (-> selected first))]
(when (or (and (= (count selected) 1)
(= (:id shape) edition)
(cph/path-shape? shape))
(and (not (cph/text-shape? shape))
(not (cph/frame-shape? shape))))
(and (some? drawing-obj)
(cph/path-shape? drawing-obj)
(not= :curve (:tool drawing))))

View file

@ -368,12 +368,22 @@
(when (some? node)
(.blur node)))
;; List of dom events for different browsers to detect the exit of fullscreen mode
(def fullscreen-events
["fullscreenchange" "mozfullscreenchange" "MSFullscreenChange" "webkitfullscreenchange"])
(defn fullscreen?
[]
(cond
(obj/in? globals/document "webkitFullscreenElement")
(boolean (.-webkitFullscreenElement globals/document))
(obj/in? globals/document "mozFullScreen")
(boolean (.-mozFullScreen globals/document))
(obj/in? globals/document "msFullscreenElement")
(boolean (.-msFullscreenElement globals/document))
(obj/in? globals/document "fullscreenElement")
(boolean (.-fullscreenElement globals/document))