mirror of
https://github.com/penpot/penpot.git
synced 2026-05-30 04:08:08 +00:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
9dd7835815
@ -168,7 +168,7 @@
|
|||||||
(contains? (meta changes) ::file-data)
|
(contains? (meta changes) ::file-data)
|
||||||
"Call (with-file-data) before using this function"))
|
"Call (with-file-data) before using this function"))
|
||||||
|
|
||||||
(defn- lookup-objects
|
(defn lookup-objects
|
||||||
[changes]
|
[changes]
|
||||||
(let [data (::file-data (meta changes))]
|
(let [data (::file-data (meta changes))]
|
||||||
(dm/get-in data [:pages-index uuid/zero :objects])))
|
(dm/get-in data [:pages-index uuid/zero :objects])))
|
||||||
|
|||||||
@ -148,10 +148,6 @@
|
|||||||
;; Enable performance logs in devconsole (disabled by default)
|
;; Enable performance logs in devconsole (disabled by default)
|
||||||
:perf-logs
|
:perf-logs
|
||||||
|
|
||||||
;; Used for designate features that will be available in the next
|
|
||||||
;; release
|
|
||||||
:canary
|
|
||||||
|
|
||||||
;; Security layer middleware that filters request by fetch
|
;; Security layer middleware that filters request by fetch
|
||||||
;; metadata headers
|
;; metadata headers
|
||||||
:sec-fetch-metadata-middleware
|
:sec-fetch-metadata-middleware
|
||||||
|
|||||||
@ -76,23 +76,26 @@
|
|||||||
|
|
||||||
(defn generate-update-shapes
|
(defn generate-update-shapes
|
||||||
[changes ids update-fn objects {:keys [attrs changed-sub-attr ignore-tree ignore-touched with-objects?]}]
|
[changes ids update-fn objects {:keys [attrs changed-sub-attr ignore-tree ignore-touched with-objects?]}]
|
||||||
(let [changes (reduce
|
(let [changes
|
||||||
(fn [changes id]
|
(->> ids
|
||||||
(let [opts {:attrs attrs
|
(reduce
|
||||||
:ignore-geometry? (get ignore-tree id)
|
(fn [changes id]
|
||||||
:ignore-touched ignore-touched
|
(let [opts {:attrs attrs
|
||||||
:with-objects? with-objects?}]
|
:ignore-geometry? (get ignore-tree id)
|
||||||
(pcb/update-shapes changes [id] update-fn (d/without-nils opts))))
|
:ignore-touched ignore-touched
|
||||||
(-> changes
|
:with-objects? with-objects?}]
|
||||||
(pcb/with-objects objects))
|
(pcb/update-shapes changes [id] update-fn (d/without-nils opts))))
|
||||||
ids)
|
(cond-> changes
|
||||||
grid-ids (->> ids (filter (partial ctl/grid-layout? objects)))
|
(some? objects) (pcb/with-objects objects))))
|
||||||
changes (-> changes
|
grid-ids
|
||||||
(pcb/update-shapes grid-ids ctl/assign-cell-positions {:with-objects? true})
|
(->> ids (filter (partial ctl/grid-layout? objects)))
|
||||||
(pcb/reorder-grid-children ids)
|
|
||||||
(cond->
|
changes
|
||||||
(not ignore-touched)
|
(-> changes
|
||||||
(generate-unapply-tokens objects changed-sub-attr)))]
|
(pcb/update-shapes grid-ids ctl/assign-cell-positions {:with-objects? true})
|
||||||
|
(pcb/reorder-grid-children ids)
|
||||||
|
(cond-> (not ignore-touched)
|
||||||
|
(generate-unapply-tokens objects changed-sub-attr)))]
|
||||||
changes))
|
changes))
|
||||||
|
|
||||||
(defn- generate-update-shape-flags
|
(defn- generate-update-shape-flags
|
||||||
|
|||||||
@ -491,60 +491,50 @@
|
|||||||
([parent-id objects children]
|
([parent-id objects children]
|
||||||
(find-valid-parent-and-frame-ids parent-id objects children false nil))
|
(find-valid-parent-and-frame-ids parent-id objects children false nil))
|
||||||
([parent-id objects children pasting? libraries]
|
([parent-id objects children pasting? libraries]
|
||||||
(letfn [(get-frame [parent-id]
|
(letfn [(get-frame [pid]
|
||||||
(if (cfh/frame-shape? objects parent-id) parent-id (get-in objects [parent-id :frame-id])))]
|
(if (cfh/frame-shape? objects pid) pid (get-in objects [pid :frame-id])))]
|
||||||
(let [parent (get objects parent-id)
|
;; `descendants`, variant-id set, etc. depend only on the moved shapes, not on the
|
||||||
|
;; candidate parent. Computing them once per drag (this fn is hot during move)
|
||||||
;; We need to check only the top shapes
|
;; avoids O(depth * subtree) work when walking invalid ancestors — common with
|
||||||
children-ids (set (map :id children))
|
;; variants and nested components.
|
||||||
|
(let [children-ids (into #{} (map :id) children)
|
||||||
top-children (remove #(contains? children-ids (:parent-id %)) children)
|
top-children (remove #(contains? children-ids (:parent-id %)) children)
|
||||||
|
all-main? (every? ctk/main-instance? top-children)
|
||||||
;; We can always move the children to the parent they already have.
|
get-variant-id
|
||||||
;; But if we are pasting, those are new items, so it is considered a change
|
(fn [shape]
|
||||||
no-changes?
|
(when (:component-id shape)
|
||||||
(and (every? #(= parent-id (:parent-id %)) top-children)
|
(-> (get-component-from-shape shape libraries)
|
||||||
(not pasting?))
|
:variant-id)))
|
||||||
|
|
||||||
;; Are all the top-children a main-instance of a component?
|
|
||||||
all-main?
|
|
||||||
(every? ctk/main-instance? top-children)
|
|
||||||
|
|
||||||
ascendants (cfh/get-parents-with-self objects parent-id)
|
|
||||||
any-main-ascendant (some ctk/main-instance? ascendants)
|
|
||||||
any-variant-container-ascendant (some ctk/is-variant-container? ascendants)
|
|
||||||
|
|
||||||
get-variant-id (fn [shape]
|
|
||||||
(when (:component-id shape)
|
|
||||||
(-> (get-component-from-shape shape libraries)
|
|
||||||
:variant-id)))
|
|
||||||
|
|
||||||
descendants (mapcat #(cfh/get-children-with-self objects %) children-ids)
|
descendants (mapcat #(cfh/get-children-with-self objects %) children-ids)
|
||||||
any-variant-container-descendant (some ctk/is-variant-container? descendants)
|
any-variant-container-descendant (some ctk/is-variant-container? descendants)
|
||||||
descendants-variant-ids-set (->> descendants
|
descendants-variant-ids-set (into #{} (map get-variant-id) descendants)
|
||||||
(map get-variant-id)
|
;; Same as (some #(some ctk/main-instance? (cfh/get-children-with-self objects (:id %)))
|
||||||
set)
|
;; children) but a single walk over `descendants`.
|
||||||
any-main-descendant
|
any-main-descendant (some ctk/main-instance? descendants)]
|
||||||
(some
|
(loop [parent-id parent-id]
|
||||||
(fn [shape]
|
(let [parent (get objects parent-id)
|
||||||
(some ctk/main-instance? (cfh/get-children-with-self objects (:id shape))))
|
no-changes?
|
||||||
children)]
|
(and (every? #(= parent-id (:parent-id %)) top-children)
|
||||||
|
(not pasting?))
|
||||||
(if (or no-changes?
|
ascendants (cfh/get-parents-with-self objects parent-id)
|
||||||
(and (not (invalid-structure-for-component? objects parent children pasting? libraries))
|
any-main-ascendant (some ctk/main-instance? ascendants)
|
||||||
;; If we are moving (not pasting) into a main component, no descendant can be main
|
any-variant-container-ascendant (some ctk/is-variant-container? ascendants)]
|
||||||
(or pasting? (nil? any-main-descendant) (not (ctk/main-instance? parent)))
|
(if (or no-changes?
|
||||||
;; Don't allow variant-container inside variant container nor main
|
(and (not (invalid-structure-for-component? objects parent children pasting? libraries))
|
||||||
(or (not any-variant-container-descendant)
|
;; If we are moving (not pasting) into a main component, no descendant can be main
|
||||||
(and (not any-variant-container-ascendant) (not any-main-ascendant)))
|
(or pasting? (nil? any-main-descendant) (not (ctk/main-instance? parent)))
|
||||||
;; If the parent is a variant-container, all the items should be main
|
;; Don't allow variant-container inside variant container nor main
|
||||||
(or (not (ctk/is-variant-container? parent)) all-main?)
|
(or (not any-variant-container-descendant)
|
||||||
;; If we are pasting, the parent can't be a "brother" of any of the pasted items,
|
(and (not any-variant-container-ascendant) (not any-main-ascendant)))
|
||||||
;; so not have the same variant-id of any descendant
|
;; If the parent is a variant-container, all the items should be main
|
||||||
(or (not pasting?)
|
(or (not (ctk/is-variant-container? parent)) all-main?)
|
||||||
(not (ctk/is-variant? parent))
|
;; If we are pasting, the parent can't be a "brother" of any of the pasted items,
|
||||||
(not (contains? descendants-variant-ids-set (:variant-id parent))))))
|
;; so not have the same variant-id of any descendant
|
||||||
[parent-id (get-frame parent-id)]
|
(or (not pasting?)
|
||||||
(recur (:parent-id parent) objects children pasting? libraries))))))
|
(not (ctk/is-variant? parent))
|
||||||
|
(not (contains? descendants-variant-ids-set (:variant-id parent))))))
|
||||||
|
[parent-id (get-frame parent-id)]
|
||||||
|
(recur (:parent-id parent)))))))))
|
||||||
|
|
||||||
;; --- SHAPE UPDATE
|
;; --- SHAPE UPDATE
|
||||||
|
|
||||||
|
|||||||
@ -46,11 +46,113 @@
|
|||||||
|
|
||||||
(cond-> changes add-undo-group? (assoc :undo-group undo-group))))
|
(cond-> changes add-undo-group? (assoc :undo-group undo-group))))
|
||||||
|
|
||||||
(defn update-shapes
|
(defn update-shapes-buffer-start
|
||||||
([ids update-fn] (update-shapes ids update-fn nil))
|
[]
|
||||||
|
(ptk/reify ::update-shapes-buffer-start
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc state ::update-shapes-buffer true))))
|
||||||
|
|
||||||
|
(defn update-shapes-buffer-stop
|
||||||
|
[]
|
||||||
|
(ptk/reify ::update-shapes-buffer-stop
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [_ state]
|
||||||
|
(assoc state ::update-shapes-buffer false))))
|
||||||
|
|
||||||
|
(defn update-shapes-buffer-commit
|
||||||
|
[]
|
||||||
|
(ptk/reify ::update-shapes-buffer-commit
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state _]
|
||||||
|
(->> (get state ::update-shapes-buffer-changes)
|
||||||
|
(vals)
|
||||||
|
(map dch/commit-changes)
|
||||||
|
(rx/from)))))
|
||||||
|
|
||||||
|
;; Looks for the objects data in the state, if there is an "in progress"
|
||||||
|
;; update-shapes-buffer will return the objeccts inside the current changes
|
||||||
|
;; to be applied.
|
||||||
|
(defn lookup-changed-objects
|
||||||
|
[state page-id]
|
||||||
|
(let [changes-objects
|
||||||
|
(-> (get-in state [::update-shapes-buffer-changes page-id])
|
||||||
|
(pcb/lookup-objects))]
|
||||||
|
(or changes-objects (dsh/lookup-page-objects state page-id))))
|
||||||
|
|
||||||
|
;; Accumulates the update shapes changes into a single commit-changes
|
||||||
|
;; The accumulation is marked between the events `start` and `stop` in between
|
||||||
|
;; those events all the `update-shapes` will be agregated together with this event.
|
||||||
|
;; After a `stop` arrives the `commit` will send the changes at the same time.
|
||||||
|
(defn update-shapes-buffer
|
||||||
|
([ids update-fn]
|
||||||
|
(update-shapes-buffer ids update-fn nil))
|
||||||
([ids update-fn
|
([ids update-fn
|
||||||
{:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id
|
{:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id
|
||||||
ignore-touched undo-group with-objects? changed-sub-attr translation?]
|
ignore-touched undo-group with-objects? changed-sub-attr translation?]
|
||||||
|
:or {reg-objects? false
|
||||||
|
save-undo? true
|
||||||
|
stack-undo? false
|
||||||
|
ignore-touched false
|
||||||
|
with-objects? false}
|
||||||
|
:as props}]
|
||||||
|
(let [cur-event (js/Symbol)]
|
||||||
|
(ptk/reify ::update-shapes-buffer
|
||||||
|
ptk/UpdateEvent
|
||||||
|
(update [it state]
|
||||||
|
(if (nil? (::update-shapes-buffer-event state))
|
||||||
|
(assoc state ::update-shapes-buffer-event cur-event)
|
||||||
|
|
||||||
|
(let [page-id (or page-id (get state :current-page-id))
|
||||||
|
objects (dsh/lookup-page-objects state page-id)]
|
||||||
|
(-> state
|
||||||
|
(update-in
|
||||||
|
[::update-shapes-buffer-changes page-id]
|
||||||
|
(fn [changes]
|
||||||
|
(-> (or changes
|
||||||
|
(-> (pcb/empty-changes it page-id)
|
||||||
|
(pcb/with-objects objects)
|
||||||
|
(pcb/set-save-undo? save-undo?)
|
||||||
|
(pcb/set-stack-undo? stack-undo?)
|
||||||
|
(cond-> undo-group
|
||||||
|
(pcb/set-undo-group undo-group))))
|
||||||
|
(cls/generate-update-shapes
|
||||||
|
ids
|
||||||
|
update-fn
|
||||||
|
nil
|
||||||
|
{:attrs attrs
|
||||||
|
:changed-sub-attr changed-sub-attr
|
||||||
|
:ignore-tree ignore-tree
|
||||||
|
:ignore-touched ignore-touched
|
||||||
|
:with-objects? with-objects?})
|
||||||
|
(cond-> reg-objects? (pcb/resize-parents ids))
|
||||||
|
(pcb/set-translation? translation?))))))))
|
||||||
|
|
||||||
|
ptk/WatchEvent
|
||||||
|
(watch [_ state stream]
|
||||||
|
(if (= (::update-shapes-buffer-event state) cur-event)
|
||||||
|
(let [stopper (->> stream (rx/filter (ptk/type? ::update-shapes-buffer-stop)))]
|
||||||
|
(rx/concat
|
||||||
|
(rx/merge
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? ::update-shapes-buffer))
|
||||||
|
(rx/take-until stopper)
|
||||||
|
(rx/last)
|
||||||
|
(rx/map update-shapes-buffer-commit))
|
||||||
|
(rx/of (update-shapes-buffer ids update-fn props)))
|
||||||
|
|
||||||
|
(rx/of #(dissoc %
|
||||||
|
::update-shapes-buffer-changes
|
||||||
|
::update-shapes-buffer-event))))
|
||||||
|
(rx/empty)))))))
|
||||||
|
|
||||||
|
(defn update-shapes
|
||||||
|
([ids update-fn]
|
||||||
|
(update-shapes ids update-fn nil))
|
||||||
|
([ids update-fn
|
||||||
|
{:as props
|
||||||
|
:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id
|
||||||
|
ignore-touched undo-group with-objects? changed-sub-attr translation?]
|
||||||
:or {reg-objects? false
|
:or {reg-objects? false
|
||||||
save-undo? true
|
save-undo? true
|
||||||
stack-undo? false
|
stack-undo? false
|
||||||
@ -63,49 +165,53 @@
|
|||||||
(ptk/reify ::update-shapes
|
(ptk/reify ::update-shapes
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
(let [page-id (or page-id (get state :current-page-id))
|
|
||||||
objects (dsh/lookup-page-objects state page-id)
|
|
||||||
ids (into [] (filter some?) ids)
|
|
||||||
|
|
||||||
xf-update-layout
|
(if (::update-shapes-buffer state)
|
||||||
(comp
|
(rx/of (update-shapes-buffer ids update-fn props))
|
||||||
(map (d/getf objects))
|
|
||||||
(filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs :with-objects? with-objects?})))
|
|
||||||
(map :id))
|
|
||||||
|
|
||||||
update-layout-ids
|
(let [page-id (or page-id (get state :current-page-id))
|
||||||
(->> (into [] xf-update-layout ids)
|
objects (dsh/lookup-page-objects state page-id)
|
||||||
(not-empty))
|
ids (into [] (filter some?) ids)
|
||||||
|
|
||||||
changes
|
xf-update-layout
|
||||||
(-> (pcb/empty-changes it page-id)
|
(comp
|
||||||
(pcb/set-save-undo? save-undo?)
|
(map (d/getf objects))
|
||||||
(pcb/set-stack-undo? stack-undo?)
|
(filter #(some update-layout-attr? (pcb/changed-attrs % objects update-fn {:attrs attrs :with-objects? with-objects?})))
|
||||||
(cls/generate-update-shapes ids
|
(map :id))
|
||||||
update-fn
|
|
||||||
objects
|
|
||||||
{:attrs attrs
|
|
||||||
:changed-sub-attr changed-sub-attr
|
|
||||||
:ignore-tree ignore-tree
|
|
||||||
:ignore-touched ignore-touched
|
|
||||||
:with-objects? with-objects?})
|
|
||||||
(cond-> undo-group
|
|
||||||
(pcb/set-undo-group undo-group))
|
|
||||||
(pcb/set-translation? translation?))
|
|
||||||
|
|
||||||
changes
|
update-layout-ids
|
||||||
(add-undo-group changes state)]
|
(->> (into [] xf-update-layout ids)
|
||||||
|
(not-empty))
|
||||||
|
|
||||||
(rx/concat
|
changes
|
||||||
(if (seq (:redo-changes changes))
|
(-> (pcb/empty-changes it page-id)
|
||||||
(let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))]
|
(pcb/set-save-undo? save-undo?)
|
||||||
(rx/of (dch/commit-changes changes)))
|
(pcb/set-stack-undo? stack-undo?)
|
||||||
(rx/empty))
|
(cls/generate-update-shapes ids
|
||||||
|
update-fn
|
||||||
|
objects
|
||||||
|
{:attrs attrs
|
||||||
|
:changed-sub-attr changed-sub-attr
|
||||||
|
:ignore-tree ignore-tree
|
||||||
|
:ignore-touched ignore-touched
|
||||||
|
:with-objects? with-objects?})
|
||||||
|
(cond-> undo-group
|
||||||
|
(pcb/set-undo-group undo-group))
|
||||||
|
(pcb/set-translation? translation?))
|
||||||
|
|
||||||
;; Update layouts for properties marked
|
changes
|
||||||
(if update-layout-ids
|
(add-undo-group changes state)]
|
||||||
(rx/of (ptk/data-event :layout/update {:ids update-layout-ids}))
|
|
||||||
(rx/empty))))))))
|
(rx/concat
|
||||||
|
(if (seq (:redo-changes changes))
|
||||||
|
(let [changes (cond-> changes reg-objects? (pcb/resize-parents ids))]
|
||||||
|
(rx/of (dch/commit-changes changes)))
|
||||||
|
(rx/empty))
|
||||||
|
|
||||||
|
;; Update layouts for properties marked
|
||||||
|
(if update-layout-ids
|
||||||
|
(rx/of (ptk/data-event :layout/update {:ids update-layout-ids}))
|
||||||
|
(rx/empty)))))))))
|
||||||
|
|
||||||
(defn add-shape
|
(defn add-shape
|
||||||
([shape]
|
([shape]
|
||||||
|
|||||||
@ -98,7 +98,8 @@
|
|||||||
(udw/trigger-bounding-box-cloaking shape-ids)
|
(udw/trigger-bounding-box-cloaking shape-ids)
|
||||||
(udw/increase-rotation shape-ids value nil
|
(udw/increase-rotation shape-ids value nil
|
||||||
{:page-id page-id
|
{:page-id page-id
|
||||||
:ignore-touched true})))))))
|
:ignore-touched true
|
||||||
|
:no-wasm? true})))))))
|
||||||
|
|
||||||
(defn update-stroke-width
|
(defn update-stroke-width
|
||||||
([value shape-ids attributes] (update-stroke-width value shape-ids attributes nil))
|
([value shape-ids attributes] (update-stroke-width value shape-ids attributes nil))
|
||||||
@ -254,7 +255,8 @@
|
|||||||
(->> (rx/from shape-ids)
|
(->> (rx/from shape-ids)
|
||||||
(rx/map #(dwtr/update-position % (zipmap attributes (repeat value))
|
(rx/map #(dwtr/update-position % (zipmap attributes (repeat value))
|
||||||
{:ignore-touched true
|
{:ignore-touched true
|
||||||
:page-id page-id})))))))))
|
:page-id page-id
|
||||||
|
:no-wasm? true})))))))))
|
||||||
|
|
||||||
(defn update-layout-gap
|
(defn update-layout-gap
|
||||||
[value shape-ids attributes page-id]
|
[value shape-ids attributes page-id]
|
||||||
@ -493,8 +495,8 @@
|
|||||||
(watch [_ _ _]
|
(watch [_ _ _]
|
||||||
(when (number? value)
|
(when (number? value)
|
||||||
(rx/of
|
(rx/of
|
||||||
(when (:width attributes) (dwtr/update-dimensions shape-ids :width value {:ignore-touched true :page-id page-id}))
|
(when (:width attributes) (dwtr/update-dimensions shape-ids :width value {:ignore-touched true :page-id page-id :no-wasm? true}))
|
||||||
(when (:height attributes) (dwtr/update-dimensions shape-ids :height value {:ignore-touched true :page-id page-id}))))))))
|
(when (:height attributes) (dwtr/update-dimensions shape-ids :height value {:ignore-touched true :page-id page-id :no-wasm? true}))))))))
|
||||||
|
|
||||||
(defn- attributes->actions
|
(defn- attributes->actions
|
||||||
[{:keys [value shape-ids attributes page-id]}]
|
[{:keys [value shape-ids attributes page-id]}]
|
||||||
|
|||||||
@ -195,9 +195,20 @@
|
|||||||
(rx/of (-> (ts/resolve-tokens tokens-tree)
|
(rx/of (-> (ts/resolve-tokens tokens-tree)
|
||||||
(d/update-vals #(update % :resolved-value ts/tokenscript-symbols->penpot-unit))))
|
(d/update-vals #(update % :resolved-value ts/tokenscript-symbols->penpot-unit))))
|
||||||
(sd/resolve-tokens tokens-tree))
|
(sd/resolve-tokens tokens-tree))
|
||||||
(rx/mapcat (fn [sd-tokens]
|
(rx/mapcat
|
||||||
(let [undo-id (js/Symbol)]
|
(fn [sd-tokens]
|
||||||
(rx/concat
|
(let [undo-id (js/Symbol)]
|
||||||
(rx/of (dwu/start-undo-transaction undo-id :timeout false))
|
(rx/concat
|
||||||
(propagate-tokens state sd-tokens)
|
(rx/of (dwu/start-undo-transaction undo-id :timeout false))
|
||||||
(rx/of (dwu/commit-undo-transaction undo-id)))))))))))
|
|
||||||
|
;; FIXME: now the tokens propagations is done by accumulating the update-shapes
|
||||||
|
;; into a single commit-changes. This is not really the best way, the token application
|
||||||
|
;; should be done with a changes_builder and sending only one `commit-changes` instead
|
||||||
|
;; of creating lots of `update-shapes`.
|
||||||
|
(rx/of (dwsh/update-shapes-buffer-start))
|
||||||
|
(->> (propagate-tokens state sd-tokens)
|
||||||
|
(rx/catch #(rx/concat
|
||||||
|
(rx/of (dwsh/update-shapes-buffer-stop))
|
||||||
|
(rx/throw %))))
|
||||||
|
(rx/of (dwsh/update-shapes-buffer-stop))
|
||||||
|
(rx/of (dwu/commit-undo-transaction undo-id)))))))))))
|
||||||
|
|||||||
@ -33,6 +33,7 @@
|
|||||||
[app.main.data.workspace.collapse :as dwc]
|
[app.main.data.workspace.collapse :as dwc]
|
||||||
[app.main.data.workspace.modifiers :as dwm]
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
[app.main.data.workspace.selection :as dws]
|
[app.main.data.workspace.selection :as dws]
|
||||||
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.main.features :as features]
|
[app.main.features :as features]
|
||||||
[app.main.snap :as snap]
|
[app.main.snap :as snap]
|
||||||
@ -373,7 +374,7 @@
|
|||||||
"Change size of shapes, from the sidebar options form
|
"Change size of shapes, from the sidebar options form
|
||||||
(will ignore pixel snap)"
|
(will ignore pixel snap)"
|
||||||
([ids attr value] (update-dimensions ids attr value nil))
|
([ids attr value] (update-dimensions ids attr value nil))
|
||||||
([ids attr value options]
|
([ids attr value {:keys [no-wasm?] :as options}]
|
||||||
(assert (number? value))
|
(assert (number? value))
|
||||||
(assert (every? uuid? ids)
|
(assert (every? uuid? ids)
|
||||||
"expected valid coll of uuids")
|
"expected valid coll of uuids")
|
||||||
@ -388,7 +389,7 @@
|
|||||||
(get state :current-page-id))
|
(get state :current-page-id))
|
||||||
|
|
||||||
objects
|
objects
|
||||||
(dsh/lookup-page-objects state page-id)
|
(dwsh/lookup-changed-objects state page-id)
|
||||||
|
|
||||||
get-modifier
|
get-modifier
|
||||||
(fn [shape]
|
(fn [shape]
|
||||||
@ -408,7 +409,7 @@
|
|||||||
|
|
||||||
modif-tree (dwm/build-modif-tree ids objects get-modifier)]
|
modif-tree (dwm/build-modif-tree ids objects get-modifier)]
|
||||||
|
|
||||||
(if (features/active-feature? state "render-wasm/v1")
|
(if (and (features/active-feature? state "render-wasm/v1") (not no-wasm?))
|
||||||
(rx/of (dwm/apply-wasm-modifiers modif-tree (assoc options :ignore-snap-pixel true)))
|
(rx/of (dwm/apply-wasm-modifiers modif-tree (assoc options :ignore-snap-pixel true)))
|
||||||
|
|
||||||
(let [modif-tree (gm/set-objects-modifiers modif-tree objects)]
|
(let [modif-tree (gm/set-objects-modifiers modif-tree objects)]
|
||||||
@ -532,11 +533,11 @@
|
|||||||
"Rotate shapes a fixed angle, from a keyboard action."
|
"Rotate shapes a fixed angle, from a keyboard action."
|
||||||
([ids rotation]
|
([ids rotation]
|
||||||
(increase-rotation ids rotation nil))
|
(increase-rotation ids rotation nil))
|
||||||
([ids rotation {:keys [center delta?] :as params} & {:as options}]
|
([ids rotation {:keys [center delta?] :as params} & {:keys [no-wasm?] :as options}]
|
||||||
(ptk/reify ::increase-rotation
|
(ptk/reify ::increase-rotation
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state _]
|
(watch [_ state _]
|
||||||
(if (features/active-feature? state "render-wasm/v1")
|
(if (and (features/active-feature? state "render-wasm/v1") (not no-wasm?))
|
||||||
(let [objects (dsh/lookup-page-objects state)
|
(let [objects (dsh/lookup-page-objects state)
|
||||||
|
|
||||||
get-modifier
|
get-modifier
|
||||||
@ -558,6 +559,7 @@
|
|||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (dwm/set-delta-rotation-modifiers rotation shapes (assoc params :page-id page-id)))
|
(rx/of (dwm/set-delta-rotation-modifiers rotation shapes (assoc params :page-id page-id)))
|
||||||
(rx/of (dwm/apply-modifiers options)))))))))
|
(rx/of (dwm/apply-modifiers options)))))))))
|
||||||
|
|
||||||
;; -- Move ----------------------------------------------------------
|
;; -- Move ----------------------------------------------------------
|
||||||
|
|
||||||
(declare start-move)
|
(declare start-move)
|
||||||
@ -653,6 +655,10 @@
|
|||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [prev-cell-data (volatile! nil)
|
(let [prev-cell-data (volatile! nil)
|
||||||
|
;; Cache the resolved valid parent while hovering the same raw target frame.
|
||||||
|
;; `find-valid-parent-and-frame-ids` may walk many ancestors for variants/components,
|
||||||
|
;; and the result is stable during the gesture (objects/libraries are constant here).
|
||||||
|
find-valid-for-raw-cache (volatile! {:raw nil :pair nil})
|
||||||
page-id (:current-page-id state)
|
page-id (:current-page-id state)
|
||||||
libraries (dsh/lookup-libraries state)
|
libraries (dsh/lookup-libraries state)
|
||||||
objects (dsh/lookup-page-objects state page-id)
|
objects (dsh/lookup-page-objects state page-id)
|
||||||
@ -713,15 +719,22 @@
|
|||||||
(fn [[move-vector mod?]]
|
(fn [[move-vector mod?]]
|
||||||
(let [position (gpt/add from-position move-vector)
|
(let [position (gpt/add from-position move-vector)
|
||||||
exclude-frames (if mod? exclude-frames exclude-frames-siblings)
|
exclude-frames (if mod? exclude-frames exclude-frames-siblings)
|
||||||
target-frame (ctst/top-nested-frame objects position exclude-frames)
|
raw-target (ctst/top-nested-frame objects position exclude-frames)
|
||||||
[target-frame _] (ctn/find-valid-parent-and-frame-ids target-frame objects shapes false libraries)
|
cache @find-valid-for-raw-cache
|
||||||
|
[target-frame _]
|
||||||
|
(if (= raw-target (:raw cache))
|
||||||
|
(:pair cache)
|
||||||
|
(let [pair (ctn/find-valid-parent-and-frame-ids raw-target objects shapes false libraries)]
|
||||||
|
(vreset! find-valid-for-raw-cache {:raw raw-target :pair pair})
|
||||||
|
pair))
|
||||||
flex-layout? (ctl/flex-layout? objects target-frame)
|
flex-layout? (ctl/flex-layout? objects target-frame)
|
||||||
grid-layout? (ctl/grid-layout? objects target-frame)
|
grid-layout? (ctl/grid-layout? objects target-frame)
|
||||||
drop-index (when flex-layout? (gslf/get-drop-index target-frame objects position))
|
drop-index (when flex-layout? (gslf/get-drop-index target-frame objects position))
|
||||||
cell-data (when (and grid-layout? (not mod?)) (get-drop-cell target-frame objects position))]
|
cell-data (when (and grid-layout? (not mod?)) (get-drop-cell target-frame objects position))]
|
||||||
(array move-vector target-frame drop-index cell-data))))
|
(array move-vector target-frame drop-index cell-data))))
|
||||||
|
|
||||||
(rx/take-until stopper))
|
(rx/take-until stopper)
|
||||||
|
(rx/share))
|
||||||
|
|
||||||
modifiers-stream
|
modifiers-stream
|
||||||
(->> move-stream
|
(->> move-stream
|
||||||
@ -761,6 +774,9 @@
|
|||||||
(rx/merge
|
(rx/merge
|
||||||
(->> modifiers-stream
|
(->> modifiers-stream
|
||||||
(rx/take-until duplicate-stopper)
|
(rx/take-until duplicate-stopper)
|
||||||
|
;; Sample at a fixed cadence to keep preview smooth. Unlike a throttle,
|
||||||
|
;; this tends to avoid perceptible "jumps" while still capping WASM work.
|
||||||
|
(rx/sample 16)
|
||||||
(rx/map
|
(rx/map
|
||||||
(fn [[modifiers snap-ignore-axis]]
|
(fn [[modifiers snap-ignore-axis]]
|
||||||
(dwm/set-wasm-modifiers modifiers :snap-ignore-axis snap-ignore-axis))))
|
(dwm/set-wasm-modifiers modifiers :snap-ignore-axis snap-ignore-axis))))
|
||||||
@ -1028,7 +1044,7 @@
|
|||||||
The position is a map that can have a partial position (it means it
|
The position is a map that can have a partial position (it means it
|
||||||
can receive {:x 10}."
|
can receive {:x 10}."
|
||||||
([id position] (update-position id position nil))
|
([id position] (update-position id position nil))
|
||||||
([id position options]
|
([id position {:keys [no-wasm?] :as options}]
|
||||||
(assert (uuid? id) "expected a valid uuid for `id`")
|
(assert (uuid? id) "expected a valid uuid for `id`")
|
||||||
(assert (map? position) "expected a valid map for `position`")
|
(assert (map? position) "expected a valid map for `position`")
|
||||||
|
|
||||||
@ -1048,7 +1064,7 @@
|
|||||||
delta (calculate-delta position bbox frame)
|
delta (calculate-delta position bbox frame)
|
||||||
modifiers (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
|
modifiers (dwm/create-modif-tree [id] (ctm/move-modifiers delta))]
|
||||||
|
|
||||||
(if (features/active-feature? state "render-wasm/v1")
|
(if (and (features/active-feature? state "render-wasm/v1") (not no-wasm?))
|
||||||
(rx/of (dwm/apply-wasm-modifiers modifiers
|
(rx/of (dwm/apply-wasm-modifiers modifiers
|
||||||
{:ignore-constraints false
|
{:ignore-constraints false
|
||||||
:ignore-touched (:ignore-touched options)
|
:ignore-touched (:ignore-touched options)
|
||||||
|
|||||||
@ -17,6 +17,7 @@
|
|||||||
[app.common.types.modifiers :as ctm]
|
[app.common.types.modifiers :as ctm]
|
||||||
[app.main.data.helpers :as dsh]
|
[app.main.data.helpers :as dsh]
|
||||||
[app.main.data.workspace.modifiers :as dwm]
|
[app.main.data.workspace.modifiers :as dwm]
|
||||||
|
[app.main.data.workspace.shapes :as dwsh]
|
||||||
[app.main.data.workspace.undo :as dwu]
|
[app.main.data.workspace.undo :as dwu]
|
||||||
[app.render-wasm.api :as wasm.api]
|
[app.render-wasm.api :as wasm.api]
|
||||||
[app.render-wasm.api.fonts :as wasm.fonts]
|
[app.render-wasm.api.fonts :as wasm.fonts]
|
||||||
@ -180,20 +181,31 @@
|
|||||||
(let [font-data (wasm.fonts/make-font-data font)]
|
(let [font-data (wasm.fonts/make-font-data font)]
|
||||||
(wasm.fonts/font-stored? font-data (:emoji? font-data))))))]
|
(wasm.fonts/font-stored? font-data (:emoji? font-data))))))]
|
||||||
|
|
||||||
(if (not fonts-loaded?)
|
(if fonts-loaded?
|
||||||
(->> (rx/of (resize-wasm-text-debounce id opts))
|
|
||||||
(rx/delay 20))
|
|
||||||
(let [pass-opts (when (or (some? undo-group) (some? undo-id))
|
(let [pass-opts (when (or (some? undo-group) (some? undo-id))
|
||||||
(cond-> {}
|
(cond-> {}
|
||||||
(some? undo-group) (assoc :undo-group undo-group)
|
(some? undo-group) (assoc :undo-group undo-group)
|
||||||
(some? undo-id) (assoc :undo-id undo-id)))]
|
(some? undo-id) (assoc :undo-id undo-id)))]
|
||||||
(rx/of (resize-wasm-text-debounce-inner id pass-opts)))))))))
|
(rx/of (resize-wasm-text-debounce-inner id pass-opts)))
|
||||||
|
|
||||||
|
;; Fonts not loaded; retry after 20 msecs
|
||||||
|
(->> (rx/of (resize-wasm-text-debounce id opts))
|
||||||
|
(rx/delay 20))))))))
|
||||||
|
|
||||||
(defn resize-wasm-text-all
|
(defn resize-wasm-text-all
|
||||||
"Resize all text shapes (auto-width/auto-height) from a collection of ids."
|
"Resize all text shapes (auto-width/auto-height) from a collection of ids."
|
||||||
[ids]
|
[ids]
|
||||||
(ptk/reify ::resize-wasm-text-all
|
(ptk/reify ::resize-wasm-text-all
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ _ _]
|
(watch [_ state stream]
|
||||||
(->> (rx/from ids)
|
(let [resize-stream
|
||||||
(rx/map resize-wasm-text-debounce)))))
|
(->> (rx/from ids)
|
||||||
|
(rx/map resize-wasm-text-debounce))]
|
||||||
|
(if (::dwsh/update-shapes-buffer state)
|
||||||
|
;; If we're in the middle of a token propagation we wait until is finished to
|
||||||
|
;; recalculate the text sizes
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? ::dwsh/update-shapes-buffer-commit))
|
||||||
|
(rx/take 1)
|
||||||
|
(rx/mapcat (constantly resize-stream)))
|
||||||
|
resize-stream)))))
|
||||||
|
|||||||
@ -263,10 +263,9 @@
|
|||||||
[{:name (tr "labels.edit")
|
[{:name (tr "labels.edit")
|
||||||
:id "font-edit"
|
:id "font-edit"
|
||||||
:handler on-edit}
|
:handler on-edit}
|
||||||
(when (contains? cf/flags :canary)
|
{:name (tr "labels.download-simple")
|
||||||
{:name (tr "labels.download-simple")
|
:id "font-download"
|
||||||
:id "font-download"
|
:handler on-download}
|
||||||
:handler on-download})
|
|
||||||
{:name (tr "labels.delete")
|
{:name (tr "labels.delete")
|
||||||
:id "font-delete"
|
:id "font-delete"
|
||||||
:handler on-delete}])]
|
:handler on-delete}])]
|
||||||
|
|||||||
@ -218,6 +218,8 @@
|
|||||||
|
|
||||||
token-applied-name* (mf/use-state applied-token-name)
|
token-applied-name* (mf/use-state applied-token-name)
|
||||||
token-applied-name (deref token-applied-name*)
|
token-applied-name (deref token-applied-name*)
|
||||||
|
is-token-applied? (and (some? token-applied-name)
|
||||||
|
(not= :multiple token-applied-name))
|
||||||
|
|
||||||
focused-id* (mf/use-state nil)
|
focused-id* (mf/use-state nil)
|
||||||
focused-id (deref focused-id*)
|
focused-id (deref focused-id*)
|
||||||
@ -501,9 +503,9 @@
|
|||||||
|
|
||||||
on-scrub-pointer-down
|
on-scrub-pointer-down
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps disabled is-open is-multiple? ref min max nillable default)
|
(mf/deps disabled is-open is-multiple? ref min max nillable default is-token-applied?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(when-not (or disabled is-open is-multiple?)
|
(when-not (or disabled is-open is-multiple? is-token-applied?)
|
||||||
(let [node (mf/ref-val ref)
|
(let [node (mf/ref-val ref)
|
||||||
is-focused (and (some? node) (dom/active? node))
|
is-focused (and (some? node) (dom/active? node))
|
||||||
has-token (some? (deref token-applied-name*))]
|
has-token (some? (deref token-applied-name*))]
|
||||||
@ -518,57 +520,60 @@
|
|||||||
|
|
||||||
on-scrub-pointer-move
|
on-scrub-pointer-move
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps apply-value update-input step min max on-change-start)
|
(mf/deps apply-value update-input step min max on-change-start is-token-applied?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [state (mf/ref-val drag-state*)]
|
(when-not is-token-applied?
|
||||||
(when (or (= state :maybe-dragging) (= state :dragging))
|
(let [state (mf/ref-val drag-state*)]
|
||||||
(let [client-x (.-clientX event)
|
(when (or (= state :maybe-dragging) (= state :dragging))
|
||||||
start-x (mf/ref-val drag-start-x*)
|
(let [client-x (.-clientX event)
|
||||||
delta-x (- client-x start-x)]
|
start-x (mf/ref-val drag-start-x*)
|
||||||
(when (and (= state :maybe-dragging)
|
delta-x (- client-x start-x)]
|
||||||
(>= (js/Math.abs delta-x) 3))
|
(when (and (= state :maybe-dragging)
|
||||||
(mf/set-ref-val! drag-state* :dragging)
|
(>= (js/Math.abs delta-x) 3))
|
||||||
(dom/add-class! (dom/get-body) "cursor-drag-scrub")
|
(mf/set-ref-val! drag-state* :dragging)
|
||||||
(when (fn? on-change-start)
|
(dom/add-class! (dom/get-body) "cursor-drag-scrub")
|
||||||
(on-change-start)))
|
(when (fn? on-change-start)
|
||||||
(when (= (mf/ref-val drag-state*) :dragging)
|
(on-change-start)))
|
||||||
(let [effective-step (cond
|
(when (= (mf/ref-val drag-state*) :dragging)
|
||||||
(.-shiftKey event) (* step 10)
|
(let [effective-step (cond
|
||||||
(.-ctrlKey event) (* step 0.1)
|
(.-shiftKey event) (* step 10)
|
||||||
:else step)
|
(.-ctrlKey event) (* step 0.1)
|
||||||
steps (js/Math.round (/ delta-x 1))
|
:else step)
|
||||||
new-val (mth/clamp (+ (mf/ref-val drag-start-val*)
|
steps (js/Math.round (/ delta-x 1))
|
||||||
(* steps effective-step))
|
new-val (mth/clamp (+ (mf/ref-val drag-start-val*)
|
||||||
min max)]
|
(* steps effective-step))
|
||||||
(update-input (fmt/format-number new-val))
|
min max)]
|
||||||
(apply-value (dm/str new-val)))))))))
|
(update-input (fmt/format-number new-val))
|
||||||
|
(apply-value (dm/str new-val))))))))))
|
||||||
|
|
||||||
on-scrub-pointer-up
|
on-scrub-pointer-up
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps ref on-change-end)
|
(mf/deps ref on-change-end is-token-applied?)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [state (mf/ref-val drag-state*)]
|
(when-not is-token-applied?
|
||||||
(when (= state :maybe-dragging)
|
(let [state (mf/ref-val drag-state*)]
|
||||||
(mf/set-ref-val! drag-state* :idle)
|
(when (= state :maybe-dragging)
|
||||||
(dom/release-pointer event)
|
(mf/set-ref-val! drag-state* :idle)
|
||||||
(when-let [node (mf/ref-val ref)]
|
(dom/release-pointer event)
|
||||||
(dom/focus! node)))
|
(when-let [node (mf/ref-val ref)]
|
||||||
(when (= state :dragging)
|
(dom/focus! node)))
|
||||||
(mf/set-ref-val! drag-state* :idle)
|
(when (= state :dragging)
|
||||||
(dom/remove-class! (dom/get-body) "cursor-drag-scrub")
|
(mf/set-ref-val! drag-state* :idle)
|
||||||
(dom/release-pointer event)
|
(dom/remove-class! (dom/get-body) "cursor-drag-scrub")
|
||||||
(when (fn? on-change-end)
|
(dom/release-pointer event)
|
||||||
(on-change-end))))))
|
(when (fn? on-change-end)
|
||||||
|
(on-change-end)))))))
|
||||||
|
|
||||||
on-scrub-lost-pointer-capture
|
on-scrub-lost-pointer-capture
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps on-change-end)
|
(mf/deps on-change-end is-token-applied?)
|
||||||
(fn [_event]
|
(fn [_event]
|
||||||
(let [was-dragging (= :dragging (mf/ref-val drag-state*))]
|
(when-not is-token-applied?
|
||||||
(mf/set-ref-val! drag-state* :idle)
|
(let [was-dragging (= :dragging (mf/ref-val drag-state*))]
|
||||||
(dom/remove-class! (dom/get-body) "cursor-drag-scrub")
|
(mf/set-ref-val! drag-state* :idle)
|
||||||
(when (and was-dragging (fn? on-change-end))
|
(dom/remove-class! (dom/get-body) "cursor-drag-scrub")
|
||||||
(on-change-end)))))
|
(when (and was-dragging (fn? on-change-end))
|
||||||
|
(on-change-end))))))
|
||||||
|
|
||||||
open-dropdown
|
open-dropdown
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
@ -766,15 +771,15 @@
|
|||||||
(mf/with-effect [dropdown-options]
|
(mf/with-effect [dropdown-options]
|
||||||
(mf/set-ref-val! options-ref dropdown-options))
|
(mf/set-ref-val! options-ref dropdown-options))
|
||||||
|
|
||||||
[:div {:class [class (stl/css :input-wrapper)]
|
[:div {:class [class (stl/css-case :input-wrapper true
|
||||||
|
:resizable (not is-token-applied?))]
|
||||||
:ref wrapper-ref
|
:ref wrapper-ref
|
||||||
:on-pointer-down on-scrub-pointer-down
|
:on-pointer-down on-scrub-pointer-down
|
||||||
:on-pointer-move on-scrub-pointer-move
|
:on-pointer-move on-scrub-pointer-move
|
||||||
:on-pointer-up on-scrub-pointer-up
|
:on-pointer-up on-scrub-pointer-up
|
||||||
:on-lost-pointer-capture on-scrub-lost-pointer-capture}
|
:on-lost-pointer-capture on-scrub-lost-pointer-capture}
|
||||||
|
|
||||||
(if (and (some? token-applied-name)
|
(if is-token-applied?
|
||||||
(not= :multiple token-applied-name))
|
|
||||||
[:> token-field* token-props]
|
[:> token-field* token-props]
|
||||||
[:> input-field* input-props])
|
[:> input-field* input-props])
|
||||||
|
|
||||||
|
|||||||
@ -22,8 +22,10 @@
|
|||||||
inline-size: 100%;
|
inline-size: 100%;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:not(:focus-within) {
|
&.resizable {
|
||||||
cursor: ew-resize;
|
&:not(:focus-within) {
|
||||||
|
cursor: ew-resize;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
|
|||||||
@ -226,8 +226,7 @@
|
|||||||
[:> menu-entry* {:title (tr "workspace.shape.menu.copy-svg")
|
[:> menu-entry* {:title (tr "workspace.shape.menu.copy-svg")
|
||||||
:on-click handle-copy-svg}]
|
:on-click handle-copy-svg}]
|
||||||
|
|
||||||
(when (and (some cfh/frame-shape? shapes)
|
(when (some cfh/frame-shape? shapes)
|
||||||
(contains? cf/flags :canary))
|
|
||||||
[:> menu-entry* {:title (tr "workspace.shape.menu.copy-as-image")
|
[:> menu-entry* {:title (tr "workspace.shape.menu.copy-as-image")
|
||||||
:disabled multiple?
|
:disabled multiple?
|
||||||
:on-click handle-copy-as-image}])
|
:on-click handle-copy-as-image}])
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.math :as mth]
|
[app.common.math :as mth]
|
||||||
[app.common.path-names :as cpn]
|
[app.common.path-names :as cpn]
|
||||||
[app.config :as cf]
|
|
||||||
[app.main.constants :refer [max-input-length]]
|
[app.main.constants :refer [max-input-length]]
|
||||||
[app.main.data.event :as ev]
|
[app.main.data.event :as ev]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
@ -261,8 +260,7 @@
|
|||||||
{:name (tr "workspace.assets.edit")
|
{:name (tr "workspace.assets.edit")
|
||||||
:id "assets-edit-color"
|
:id "assets-edit-color"
|
||||||
:handler edit-color-clicked})
|
:handler edit-color-clicked})
|
||||||
(when (and (not (or multi-colors? multi-assets?))
|
(when-not (or multi-colors? multi-assets?)
|
||||||
(contains? cf/flags :canary))
|
|
||||||
{:name (tr "workspace.assets.duplicate")
|
{:name (tr "workspace.assets.duplicate")
|
||||||
:id "assets-duplicate-color"
|
:id "assets-duplicate-color"
|
||||||
:handler duplicate-color})
|
:handler duplicate-color})
|
||||||
|
|||||||
@ -10,7 +10,6 @@
|
|||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.path-names :as cpn]
|
[app.common.path-names :as cpn]
|
||||||
[app.config :as cf]
|
|
||||||
[app.main.data.event :as ev]
|
[app.main.data.event :as ev]
|
||||||
[app.main.data.modal :as modal]
|
[app.main.data.modal :as modal]
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
@ -469,8 +468,7 @@
|
|||||||
:id "assets-edit-typography"
|
:id "assets-edit-typography"
|
||||||
:handler handle-edit-typography-clicked})
|
:handler handle-edit-typography-clicked})
|
||||||
|
|
||||||
(when (and (not (or multi-typographies? multi-assets?))
|
(when-not (or multi-typographies? multi-assets?)
|
||||||
(contains? cf/flags :canary))
|
|
||||||
{:name (tr "workspace.assets.duplicate")
|
{:name (tr "workspace.assets.duplicate")
|
||||||
:id "assets-duplicate-typography"
|
:id "assets-duplicate-typography"
|
||||||
:handler handle-duplicate-typography})
|
:handler handle-duplicate-typography})
|
||||||
|
|||||||
@ -16,7 +16,6 @@
|
|||||||
[app.common.types.container :as ctn]
|
[app.common.types.container :as ctn]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
[app.common.uuid :as uuid]
|
[app.common.uuid :as uuid]
|
||||||
[app.config :as cf]
|
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.collapse :as dwc]
|
[app.main.data.workspace.collapse :as dwc]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
@ -441,20 +440,19 @@
|
|||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(mf/deps id objects)
|
(mf/deps id objects)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(when (contains? cf/flags :canary)
|
(let [shift? (kbd/shift? event)
|
||||||
(let [shift? (kbd/shift? event)
|
shape (get objects id)
|
||||||
shape (get objects id)
|
parent (get objects (:parent-id shape))
|
||||||
parent (get objects (:parent-id shape))
|
siblings (:shapes parent)
|
||||||
siblings (:shapes parent)
|
pos (d/index-of siblings id)]
|
||||||
pos (d/index-of siblings id)]
|
(when (some? pos)
|
||||||
(when (some? pos)
|
(let [;; Layers render in reverse: Tab (visually down) = dec index,
|
||||||
(let [;; Layers render in reverse: Tab (visually down) = dec index,
|
;; Shift+Tab (visually up) = inc index
|
||||||
;; Shift+Tab (visually up) = inc index
|
target-id (if shift?
|
||||||
target-id (if shift?
|
(get siblings (inc pos))
|
||||||
(get siblings (inc pos))
|
(get siblings (dec pos)))]
|
||||||
(get siblings (dec pos)))]
|
(when (some? target-id)
|
||||||
(when (some? target-id)
|
(st/emit! (dw/start-rename-shape target-id))))))))]
|
||||||
(st/emit! (dw/start-rename-shape target-id)))))))))]
|
|
||||||
|
|
||||||
(mf/with-effect [is-selected selected]
|
(mf/with-effect [is-selected selected]
|
||||||
(let [single? (= (count selected) 1)
|
(let [single? (= (count selected) 1)
|
||||||
|
|||||||
@ -78,6 +78,39 @@
|
|||||||
[selected objects modifiers]
|
[selected objects modifiers]
|
||||||
(apply-modifiers-to-objects objects (select-keys (into {} modifiers) selected)))
|
(apply-modifiers-to-objects objects (select-keys (into {} modifiers) selected)))
|
||||||
|
|
||||||
|
(defn- apply-wasm-modifiers-to-ids
|
||||||
|
"Like `apply-modifiers-to-objects`, but only updates ids in `id-set`. During WASM
|
||||||
|
drag, `wasm-modifiers` can list every propagated descendant (large variants); SVG
|
||||||
|
outlines only need geometry for `selected` / hover / highlight — not the whole page."
|
||||||
|
[objects wasm-modifiers id-set]
|
||||||
|
(if (or (empty? wasm-modifiers) (empty? id-set))
|
||||||
|
objects
|
||||||
|
(reduce
|
||||||
|
(fn [objs pair]
|
||||||
|
(let [[id t] pair]
|
||||||
|
(if (and (contains? id-set id) (contains? objs id))
|
||||||
|
(update objs id gsh/apply-transform t)
|
||||||
|
objs)))
|
||||||
|
objects
|
||||||
|
wasm-modifiers)))
|
||||||
|
|
||||||
|
(defn- outline-wasm-source-ids
|
||||||
|
"Superset of shape ids that `shape-outlines` may look up (all outline usages here)."
|
||||||
|
[base-objects selected highlighted edition hover-ids hover frame-hover]
|
||||||
|
(let [outlined-frame-id (->> hover-ids
|
||||||
|
(filter #(cfh/frame-shape? (get base-objects %)))
|
||||||
|
(remove selected)
|
||||||
|
(last))
|
||||||
|
ids (-> #{}
|
||||||
|
(into (or selected #{}))
|
||||||
|
(into (or highlighted #{}))
|
||||||
|
(into (or hover-ids #{})))]
|
||||||
|
(cond-> ids
|
||||||
|
(uuid? (:id hover)) (conj (:id hover))
|
||||||
|
(uuid? frame-hover) (conj frame-hover)
|
||||||
|
(uuid? outlined-frame-id) (conj outlined-frame-id)
|
||||||
|
(uuid? edition) (disj edition))))
|
||||||
|
|
||||||
(mf/defc viewport*
|
(mf/defc viewport*
|
||||||
[{:keys [selected wglobal layout file page palete-size]}]
|
[{:keys [selected wglobal layout file page palete-size]}]
|
||||||
(let [;; When adding data from workspace-local revisit `app.main.ui.workspace` to check
|
(let [;; When adding data from workspace-local revisit `app.main.ui.workspace` to check
|
||||||
@ -129,10 +162,6 @@
|
|||||||
(into [] (keep (d/getf objects-modified)))
|
(into [] (keep (d/getf objects-modified)))
|
||||||
(not-empty))
|
(not-empty))
|
||||||
|
|
||||||
objects-for-outlines
|
|
||||||
(mf/with-memo [base-objects wasm-modifiers]
|
|
||||||
(apply-modifiers-to-objects base-objects wasm-modifiers))
|
|
||||||
|
|
||||||
;; STATE
|
;; STATE
|
||||||
alt? (mf/use-state false)
|
alt? (mf/use-state false)
|
||||||
shift? (mf/use-state false)
|
shift? (mf/use-state false)
|
||||||
@ -178,6 +207,18 @@
|
|||||||
(when (some? parent-id)
|
(when (some? parent-id)
|
||||||
(get base-objects parent-id)))))
|
(get base-objects parent-id)))))
|
||||||
|
|
||||||
|
outline-wasm-ids
|
||||||
|
(mf/with-memo
|
||||||
|
[base-objects selected highlighted edition @hover-ids @hover @frame-hover]
|
||||||
|
(outline-wasm-source-ids base-objects selected highlighted edition @hover-ids @hover @frame-hover))
|
||||||
|
|
||||||
|
objects-for-outlines
|
||||||
|
(mf/with-memo
|
||||||
|
[base-objects wasm-modifiers outline-wasm-ids]
|
||||||
|
(if (seq wasm-modifiers)
|
||||||
|
(apply-wasm-modifiers-to-ids base-objects wasm-modifiers outline-wasm-ids)
|
||||||
|
base-objects))
|
||||||
|
|
||||||
zoom (d/check-num zoom 1)
|
zoom (d/check-num zoom 1)
|
||||||
|
|
||||||
drawing-tool (:tool drawing)
|
drawing-tool (:tool drawing)
|
||||||
|
|||||||
@ -430,10 +430,7 @@
|
|||||||
(t/is (mth/close? (get c-frame1' :width) 200))
|
(t/is (mth/close? (get c-frame1' :width) 200))
|
||||||
(t/is (mth/close? (get c-frame1' :height) 200))
|
(t/is (mth/close? (get c-frame1' :height) 200))
|
||||||
|
|
||||||
(t/is (empty? (:touched c-frame1')))
|
(t/is (empty? (:touched c-frame1'))))))))]
|
||||||
|
|
||||||
(t/testing "WASM mocks were exercised"
|
|
||||||
(t/is (pos? (thw/call-count :propagate-modifiers)))))))))]
|
|
||||||
|
|
||||||
(tohs/run-store-async
|
(tohs/run-store-async
|
||||||
store step2 events identity))))
|
store step2 events identity))))
|
||||||
|
|||||||
@ -281,9 +281,7 @@
|
|||||||
(t/is (= (:height (:applied-tokens rect-1')) (:name token-target'))))
|
(t/is (= (:height (:applied-tokens rect-1')) (:name token-target'))))
|
||||||
(t/testing "shapes width and height got updated"
|
(t/testing "shapes width and height got updated"
|
||||||
(t/is (= (:width rect-1') 100))
|
(t/is (= (:width rect-1') 100))
|
||||||
(t/is (= (:height rect-1') 100)))
|
(t/is (= (:height rect-1') 100))))))))))
|
||||||
(t/testing "WASM mocks were exercised"
|
|
||||||
(t/is (pos? (thw/call-count :propagate-modifiers)))))))))))
|
|
||||||
|
|
||||||
(t/deftest test-apply-padding
|
(t/deftest test-apply-padding
|
||||||
(t/testing "applies padding token to shapes with layout"
|
(t/testing "applies padding token to shapes with layout"
|
||||||
@ -356,9 +354,7 @@
|
|||||||
(t/is (= (:height (:applied-tokens rect-1')) (:name token-target'))))
|
(t/is (= (:height (:applied-tokens rect-1')) (:name token-target'))))
|
||||||
(t/testing "shapes width and height got updated"
|
(t/testing "shapes width and height got updated"
|
||||||
(t/is (= (:width rect-1') 100))
|
(t/is (= (:width rect-1') 100))
|
||||||
(t/is (= (:height rect-1') 100)))
|
(t/is (= (:height rect-1') 100))))))))))
|
||||||
(t/testing "WASM mocks were exercised"
|
|
||||||
(t/is (pos? (thw/call-count :propagate-modifiers)))))))))))
|
|
||||||
|
|
||||||
(t/deftest test-apply-opacity
|
(t/deftest test-apply-opacity
|
||||||
(t/testing "applies opacity token and updates the shapes opacity"
|
(t/testing "applies opacity token and updates the shapes opacity"
|
||||||
@ -443,9 +439,7 @@
|
|||||||
rect-1' (cths/get-shape file' :rect-1)]
|
rect-1' (cths/get-shape file' :rect-1)]
|
||||||
(t/is (some? (:applied-tokens rect-1')))
|
(t/is (some? (:applied-tokens rect-1')))
|
||||||
(t/is (= (:rotation (:applied-tokens rect-1')) (:name token-target')))
|
(t/is (= (:rotation (:applied-tokens rect-1')) (:name token-target')))
|
||||||
(t/is (= (:rotation rect-1') 120))
|
(t/is (= (:rotation rect-1') 120)))))))))
|
||||||
(t/testing "WASM mocks were exercised"
|
|
||||||
(t/is (pos? (thw/call-count :propagate-modifiers)))))))))))
|
|
||||||
|
|
||||||
(t/deftest test-apply-stroke-width
|
(t/deftest test-apply-stroke-width
|
||||||
(t/testing "applies stroke-width token and updates the shapes with stroke"
|
(t/testing "applies stroke-width token and updates the shapes with stroke"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user