mirror of
https://github.com/penpot/penpot.git
synced 2026-01-20 12:20:16 -05:00
Compare commits
48 Commits
hiru-fix-l
...
hiru-refac
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adc6af129c | ||
|
|
0ae4988908 | ||
|
|
a53176489a | ||
|
|
d8121364ad | ||
|
|
d4fe810813 | ||
|
|
64ddfa0c31 | ||
|
|
e8dde477a5 | ||
|
|
1b0848389c | ||
|
|
4f02cc3e86 | ||
|
|
749d60be48 | ||
|
|
bb8a523208 | ||
|
|
4d3e7f9a75 | ||
|
|
2edbc10851 | ||
|
|
5fc303a05d | ||
|
|
9a45ce80a6 | ||
|
|
3645d1af20 | ||
|
|
d2bfd98a05 | ||
|
|
ecedf46c2a | ||
|
|
73d42c03d5 | ||
|
|
e96bedc1c8 | ||
|
|
c5f37fadba | ||
|
|
8052c5f973 | ||
|
|
c499c8a323 | ||
|
|
6b9962b2b3 | ||
|
|
0a81ae1ea0 | ||
|
|
c6d71ea902 | ||
|
|
4d850ebe6e | ||
|
|
dac18e876f | ||
|
|
d016876710 | ||
|
|
ddeb540df6 | ||
|
|
7733bc4419 | ||
|
|
128fe29619 | ||
|
|
23e200dece | ||
|
|
d9375c1dd1 | ||
|
|
aeebed6ef7 | ||
|
|
498ba257b6 | ||
|
|
6edba71c12 | ||
|
|
a559e7310a | ||
|
|
cdc3367d1b | ||
|
|
8d37d63a27 | ||
|
|
95f0f63276 | ||
|
|
5cab599a06 | ||
|
|
24715a85e5 | ||
|
|
559c03550d | ||
|
|
8a9a3cbf37 | ||
|
|
f2fcd0f82f | ||
|
|
a43d439b31 | ||
|
|
bc64fdb1bc |
24
CHANGES.md
24
CHANGES.md
@@ -1,6 +1,6 @@
|
||||
# CHANGELOG
|
||||
|
||||
## :rocket: Next
|
||||
## :rocket: 1.19.0
|
||||
|
||||
### :boom: Breaking changes & Deprecations
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- Create typography style from a selected text layer[Taiga #3041](https://tree.taiga.io/project/penpot/us/3041)
|
||||
- Board as ruler origin [Taiga #4833](https://tree.taiga.io/project/penpot/us/4833)
|
||||
- Access tokens support [Taiga #4460](https://tree.taiga.io/project/penpot/us/4460)
|
||||
- Show interactions setting at the view mode [Taiga #1330](https://tree.taiga.io/project/penpot/issue/1330)
|
||||
|
||||
### :bug: Bugs fixed
|
||||
- Fix files can be opened from multiple urls [Taiga #5310](https://tree.taiga.io/project/penpot/issue/5310)
|
||||
@@ -31,6 +32,26 @@
|
||||
- Distribute fix enabled when two elements were selected (by @dfelinto) [Github #3266](https://github.com/penpot/penpot/pull/3266)
|
||||
- Distribute vertical spacing failing for overlapped text (by @dfelinto) [Github #3267](https://github.com/penpot/penpot/pull/3267)
|
||||
|
||||
## 1.18.6
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix comments navigation from workspace [Taiga #5504](https://tree.taiga.io/project/penpot/issue/5504)
|
||||
|
||||
### :sparkles: Enhancements
|
||||
|
||||
- Add the ability to overwrite internal resolver with `PENPOT_INTERNAL_RESOLVER` environment
|
||||
variable [GH #3310](https://github.com/penpot/penpot/issues/3310)
|
||||
|
||||
## 1.18.5
|
||||
|
||||
### :bug: Bugs fixed
|
||||
|
||||
- Fix add flow option in contextual menu for frames
|
||||
- Fix issues related with invitations
|
||||
- Fix problem with undefined gaps
|
||||
- Add deleted fonts auto match mechanism
|
||||
|
||||
## 1.18.4
|
||||
|
||||
### :bug: Bugs fixed
|
||||
@@ -70,6 +91,7 @@
|
||||
## 1.18.0
|
||||
|
||||
### :sparkles: New features
|
||||
|
||||
- Adds more accessibility improvements in dashboard [Taiga #4577](https://tree.taiga.io/project/penpot/us/4577)
|
||||
- Adds paddings and gaps prediction on layout creation [Taiga #4838](https://tree.taiga.io/project/penpot/task/4838)
|
||||
- Add visual feedback when proportionally scaling text elements with **K** [Taiga #3415](https://tree.taiga.io/project/penpot/us/3415)
|
||||
|
||||
@@ -169,14 +169,16 @@
|
||||
[{:keys [::db/pool] :as cfg} params]
|
||||
|
||||
(when-not (contains? cf/flags :registration)
|
||||
(if-not (contains? params :invitation-token)
|
||||
(when-not (contains? params :invitation-token)
|
||||
(ex/raise :type :restriction
|
||||
:code :registration-disabled)
|
||||
(let [invitation (tokens/verify (::main/props cfg) {:token (:invitation-token params) :iss :team-invitation})]
|
||||
(when-not (= (:email params) (:member-email invitation))
|
||||
(ex/raise :type :restriction
|
||||
:code :email-does-not-match-invitation
|
||||
:hint "email should match the invitation")))))
|
||||
:code :registration-disabled)))
|
||||
|
||||
(when (contains? params :invitation-token)
|
||||
(let [invitation (tokens/verify (::main/props cfg) {:token (:invitation-token params) :iss :team-invitation})]
|
||||
(when-not (= (:email params) (:member-email invitation))
|
||||
(ex/raise :type :restriction
|
||||
:code :email-does-not-match-invitation
|
||||
:hint "email should match the invitation"))))
|
||||
|
||||
(when-let [domains (cf/get :registration-domain-whitelist)]
|
||||
(when-not (email-domain-in-whitelist? domains (:email params))
|
||||
|
||||
@@ -490,13 +490,17 @@
|
||||
|
||||
(let [file (get-file conn file-id features)
|
||||
page-id (or page-id (-> file :data :pages first))
|
||||
page (dm/get-in file [:data :pages-index page-id])]
|
||||
page (dm/get-in file [:data :pages-index page-id])
|
||||
page (if (pmap/pointer-map? page)
|
||||
(deref page)
|
||||
page)]
|
||||
(cond-> (prune-thumbnails page)
|
||||
(uuid? object-id)
|
||||
(prune-objects object-id))))
|
||||
|
||||
(sm/def! ::get-page
|
||||
[:map {:title "GetPage"}
|
||||
[:file-id ::sm/uuid]
|
||||
[:page-id {:optional true} ::sm/uuid]
|
||||
[:object-id {:optional true} ::sm/uuid]
|
||||
[:features {:optional true} ::features]])
|
||||
@@ -516,7 +520,10 @@
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(dm/with-open [conn (db/open pool)]
|
||||
(check-read-permissions! conn profile-id file-id)
|
||||
(get-page conn params)))
|
||||
|
||||
(binding [pmap/*load-fn* (partial load-pointer conn file-id)]
|
||||
(get-page conn params))))
|
||||
|
||||
|
||||
|
||||
;; --- COMMAND QUERY: get-team-shared-files
|
||||
@@ -892,7 +899,7 @@
|
||||
;; TODO: improve naming
|
||||
|
||||
(sv/defmethod ::update-file-library-sync-status
|
||||
"Update the synchronization statos of a file->library link"
|
||||
"Update the synchronization status of a file->library link"
|
||||
{::doc/added "1.17"}
|
||||
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id] :as params}]
|
||||
(db/with-atomic [conn pool]
|
||||
|
||||
@@ -8,14 +8,14 @@
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.schema :as sm]
|
||||
[app.db :as db]
|
||||
[app.rpc :as-alias rpc]
|
||||
[app.rpc.commands.comments :as comments]
|
||||
[app.rpc.commands.files :as files]
|
||||
[app.rpc.cond :as-alias cond]
|
||||
[app.rpc.doc :as-alias doc]
|
||||
[app.util.services :as sv]
|
||||
[clojure.spec.alpha :as s]))
|
||||
[app.util.services :as sv]))
|
||||
|
||||
;; --- QUERY: View Only Bundle
|
||||
|
||||
@@ -79,18 +79,19 @@
|
||||
:always
|
||||
(update :data select-keys [:id :options :pages :pages-index :components]))))))
|
||||
|
||||
(s/def ::get-view-only-bundle
|
||||
(s/keys :req-un [::files/file-id]
|
||||
:opt-un [::files/share-id
|
||||
::files/features]
|
||||
:opt [::rpc/profile-id]))
|
||||
(sm/def! ::get-view-only-bundle
|
||||
[:map {:title "get-view-only-bundle"}
|
||||
[:file-id ::sm/uuid]
|
||||
[:share-id {:optional true} ::sm/uuid]
|
||||
[:features {:optional true} ::files/features]])
|
||||
|
||||
(sv/defmethod ::get-view-only-bundle
|
||||
{::rpc/auth false
|
||||
::cond/get-object #(files/get-minimal-file %1 (:file-id %2))
|
||||
::cond/key-fn files/get-file-etag
|
||||
::cond/reuse-key? true
|
||||
::doc/added "1.17"}
|
||||
::doc/added "1.17"
|
||||
::sm/params ::get-view-only-bundle}
|
||||
[{:keys [::db/pool]} {:keys [::rpc/profile-id] :as params}]
|
||||
(dm/with-open [conn (db/open pool)]
|
||||
(get-view-only-bundle conn (assoc params :profile-id profile-id))))
|
||||
|
||||
@@ -216,8 +216,9 @@
|
||||
[:type [:= :del-typography]]
|
||||
[:id ::sm/uuid]]]]])
|
||||
|
||||
|
||||
|
||||
(sm/def! ::changes
|
||||
[:sequential {:gen/max 2} ::change])
|
||||
|
||||
(def change?
|
||||
(sm/pred-fn ::change))
|
||||
|
||||
@@ -302,34 +303,34 @@
|
||||
|
||||
(defmethod process-change :mod-obj
|
||||
[data {:keys [id page-id component-id operations]}]
|
||||
(let [objects (if page-id
|
||||
(-> data :pages-index (get page-id) :objects)
|
||||
(-> data :components (get component-id) :objects))
|
||||
(let [changed? (atom false)
|
||||
|
||||
modified-component-ids (atom #{})
|
||||
|
||||
on-touched (fn [shape]
|
||||
;; When a shape is modified, if it belongs to a main component instance,
|
||||
;; the component needs to be marked as modified.
|
||||
(let [component-root (ctn/get-component-shape objects shape {:allow-main? true})]
|
||||
(when (ctk/main-instance? component-root)
|
||||
(swap! modified-component-ids conj (:component-id component-root)))))
|
||||
process-and-register (partial process-operation
|
||||
(fn [_shape] (reset! changed? true)))
|
||||
|
||||
update-fn (fn [objects]
|
||||
(if-let [obj (get objects id)]
|
||||
(let [result (reduce (partial process-operation on-touched) obj operations)]
|
||||
(assoc objects id result))
|
||||
objects))
|
||||
(d/update-when objects id
|
||||
#(reduce process-and-register % operations)))
|
||||
|
||||
modify-components (fn [data]
|
||||
(reduce ctkl/set-component-modified
|
||||
data @modified-component-ids))]
|
||||
check-modify-component (fn [data]
|
||||
(if @changed?
|
||||
;; When a shape is modified, if it belongs to a main component instance,
|
||||
;; the component needs to be marked as modified.
|
||||
(let [objects (if page-id
|
||||
(-> data :pages-index (get page-id) :objects)
|
||||
(-> data :components (get component-id) :objects))
|
||||
shape (get objects id)
|
||||
component-root (ctn/get-component-shape objects shape {:allow-main? true})]
|
||||
(if (and (some? component-root) (ctk/main-instance? component-root))
|
||||
(ctkl/set-component-modified data (:component-id component-root))
|
||||
data))
|
||||
data))]
|
||||
|
||||
(as-> data $
|
||||
(if page-id
|
||||
(d/update-in-when $ [:pages-index page-id :objects] update-fn)
|
||||
(d/update-in-when $ [:components component-id :objects] update-fn))
|
||||
(modify-components $))))
|
||||
(check-modify-component $))))
|
||||
|
||||
(defmethod process-change :del-obj
|
||||
[data {:keys [page-id component-id id ignore-touched]}]
|
||||
@@ -583,7 +584,7 @@
|
||||
;; === Operations
|
||||
|
||||
(defmethod process-operation :set
|
||||
[on-touched shape op]
|
||||
[on-changed shape op]
|
||||
(let [attr (:attr op)
|
||||
group (get component-sync-attrs attr)
|
||||
val (:val op)
|
||||
@@ -608,10 +609,10 @@
|
||||
(gsh/close-attrs? attr val shape-val 1)
|
||||
(gsh/close-attrs? attr val shape-val))]
|
||||
|
||||
(when (and group (not ignore) (not equal?)
|
||||
(not (and ignore-geometry is-geometry?)))
|
||||
;; Notify touched even if it's not copy, because it may be a main instance
|
||||
(on-touched shape))
|
||||
;; Notify when value has changed, except when it has not moved relative to the
|
||||
;; component head.
|
||||
(when (and group (not equal?) (not (and ignore-geometry is-geometry?)))
|
||||
(on-changed shape))
|
||||
|
||||
(cond-> shape
|
||||
;; Depending on the origin of the attribute change, we need or not to
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
@@ -216,9 +217,14 @@
|
||||
|
||||
([changes obj {:keys [index ignore-touched] :or {index ::undefined ignore-touched false}}]
|
||||
(assert-page-id changes)
|
||||
(assert-objects changes)
|
||||
(let [obj (cond-> obj
|
||||
(not= index ::undefined)
|
||||
(assoc ::index index))
|
||||
|
||||
objects (lookup-objects changes)
|
||||
parent (get objects (:parent-id obj))
|
||||
|
||||
add-change
|
||||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
@@ -232,10 +238,20 @@
|
||||
del-change
|
||||
{:type :del-obj
|
||||
:id (:id obj)
|
||||
:page-id (::page-id (meta changes))}]
|
||||
:page-id (::page-id (meta changes))}
|
||||
|
||||
restore-touched-change
|
||||
{:type :mod-obj
|
||||
:page-id (::page-id (meta changes))
|
||||
:id (:id parent)
|
||||
:operations [{:type :set-touched
|
||||
:touched (:touched parent)}]}]
|
||||
|
||||
(-> changes
|
||||
(update :redo-changes conj add-change)
|
||||
(cond->
|
||||
(and (ctk/in-component-copy? parent) (not ignore-touched))
|
||||
(update :undo-changes d/preconj restore-touched-change))
|
||||
(update :undo-changes d/preconj del-change)
|
||||
(apply-changes-local)))))
|
||||
|
||||
@@ -256,6 +272,8 @@
|
||||
(assert-page-id changes)
|
||||
(assert-objects changes)
|
||||
(let [objects (lookup-objects changes)
|
||||
parent (get objects parent-id)
|
||||
|
||||
set-parent-change
|
||||
(cond-> {:type :mov-objects
|
||||
:parent-id parent-id
|
||||
@@ -275,10 +293,20 @@
|
||||
:parent-id (:parent-id shape)
|
||||
:shapes [(:id shape)]
|
||||
:after-shape prev-sibling
|
||||
:index 0})))] ; index is used in case there is no after-shape (moving bottom shapes)
|
||||
:index 0}))) ; index is used in case there is no after-shape (moving bottom shapes)
|
||||
|
||||
restore-touched-change
|
||||
{:type :mod-obj
|
||||
:page-id (::page-id (meta changes))
|
||||
:id (:id parent)
|
||||
:operations [{:type :set-touched
|
||||
:touched (:touched parent)}]}]
|
||||
|
||||
(-> changes
|
||||
(update :redo-changes conj set-parent-change)
|
||||
(cond->
|
||||
(ctk/in-component-copy? parent)
|
||||
(update :undo-changes d/preconj restore-touched-change))
|
||||
(update :undo-changes #(reduce mk-undo-change % shapes))
|
||||
(apply-changes-local)))))
|
||||
|
||||
|
||||
@@ -39,9 +39,7 @@
|
||||
(-> parent
|
||||
(update :shapes update-parent-shapes)
|
||||
(update :shapes d/vec-without-nils)
|
||||
(cond-> (and (:shape-ref parent)
|
||||
(not= (:id parent) frame-id)
|
||||
(not ignore-touched))
|
||||
(cond-> (and (ctk/in-component-copy? parent) (not ignore-touched))
|
||||
(-> (update :touched cph/set-touched-group :shapes-group)
|
||||
(dissoc :remote-synced?)))))
|
||||
|
||||
@@ -328,6 +326,9 @@
|
||||
The list of objects are returned in tree traversal order, respecting
|
||||
the order of the children of each parent."
|
||||
|
||||
([object parent-id objects]
|
||||
(clone-object object parent-id objects (fn [object _] object) (fn [object _] object) nil false))
|
||||
|
||||
([object parent-id objects update-new-object]
|
||||
(clone-object object parent-id objects update-new-object (fn [object _] object) nil false))
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ update_flags /var/www/app/js/config.js
|
||||
|
||||
export PENPOT_BACKEND_URI=${PENPOT_BACKEND_URI:-http://penpot-backend:6060};
|
||||
export PENPOT_EXPORTER_URI=${PENPOT_EXPORTER_URI:-http://penpot-exporter:6061};
|
||||
export PENPOT_INTERNAL_RESOLVER=${PENPOT_INTERNAL_RESOLVER:-127.0.0.11};
|
||||
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI" < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
envsubst "\$PENPOT_BACKEND_URI,\$PENPOT_EXPORTER_URI,\$PENPOT_INTERNAL_RESOLVER" < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
|
||||
|
||||
exec "$@";
|
||||
|
||||
@@ -38,7 +38,7 @@ http {
|
||||
|
||||
gzip_types text/plain text/css text/javascript application/javascript application/json application/transit+json;
|
||||
|
||||
resolver 127.0.0.11;
|
||||
resolver $PENPOT_INTERNAL_RESOLVER;
|
||||
|
||||
map $http_upgrade $connection_upgrade {
|
||||
default upgrade;
|
||||
|
||||
@@ -362,11 +362,6 @@
|
||||
height: 556px;
|
||||
}
|
||||
|
||||
.info-wrapper {
|
||||
border: 1px solid $color-gray-10;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
&.project-th.library {
|
||||
height: 610px;
|
||||
width: 300px;
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
[app.main.ui :as ui]
|
||||
[app.main.ui.alert]
|
||||
[app.main.ui.confirm]
|
||||
[app.main.ui.cursors :as cursors]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.delete-shared]
|
||||
[app.main.ui.modal :refer [modal]]
|
||||
[app.main.ui.routes :as rt]
|
||||
@@ -45,7 +45,6 @@
|
||||
|
||||
(defn init-ui
|
||||
[]
|
||||
(cursors/init-styles)
|
||||
(mf/mount (mf/element ui/app) (dom/get-element "app"))
|
||||
(mf/mount (mf/element modal) (dom/get-element "modal")))
|
||||
|
||||
@@ -80,6 +79,7 @@
|
||||
(worker/init!)
|
||||
(i18n/init! cf/translations)
|
||||
(theme/init! cf/themes)
|
||||
(cur/init-styles)
|
||||
(init-ui)
|
||||
(st/emit! (initialize)))
|
||||
|
||||
|
||||
@@ -500,7 +500,7 @@
|
||||
|
||||
(defn recover-profile
|
||||
[data]
|
||||
(dm/assert! (sm/valid? ::recover-profile data))
|
||||
(dm/assert! (sm/valid? schema:recover-profile data))
|
||||
(ptk/reify ::recover-profile
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
default-local-state
|
||||
{:zoom 1
|
||||
:fullscreen? false
|
||||
:interactions-mode :hide
|
||||
:interactions-mode :show-on-click
|
||||
:interactions-show? false
|
||||
:comments-mode :all
|
||||
:comments-show :unresolved
|
||||
@@ -53,7 +53,7 @@
|
||||
[:page-id {:optional true} ::sm/uuid]])
|
||||
|
||||
(defn initialize
|
||||
[{:keys [file-id share-id] :as params}]
|
||||
[{:keys [file-id share-id interactions-show?] :as params}]
|
||||
(dm/assert! (sm/valid? schema:initialize params))
|
||||
(ptk/reify ::initialize
|
||||
ptk/UpdateEvent
|
||||
@@ -61,11 +61,12 @@
|
||||
(-> state
|
||||
(assoc :current-file-id file-id)
|
||||
(update :viewer-local
|
||||
(fn [lstate]
|
||||
(if (nil? lstate)
|
||||
default-local-state
|
||||
lstate)))
|
||||
(assoc-in [:viewer-local :share-id] share-id)))
|
||||
(fn [lstate]
|
||||
(if (nil? lstate)
|
||||
default-local-state
|
||||
lstate)))
|
||||
(assoc-in [:viewer-local :share-id] share-id)
|
||||
(assoc-in [:viewer-local :interactions-show?] interactions-show?)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
@@ -400,7 +401,14 @@
|
||||
(assoc-in [:viewer-local :interactions-show?] (case mode
|
||||
:hide false
|
||||
:show true
|
||||
:show-on-click false))))))
|
||||
:show-on-click false))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [route (:route state)
|
||||
screen (-> route :data :name keyword)
|
||||
qparams (:query-params route)
|
||||
pparams (:path-params route)]
|
||||
(rx/of (rt/nav screen pparams (assoc qparams :interactions-mode mode)))))))
|
||||
|
||||
(declare flash-done)
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
[app.main.data.workspace.drawing.common :as dwdc]
|
||||
[app.main.data.workspace.edition :as dwe]
|
||||
[app.main.data.workspace.fix-bool-contents :as fbc]
|
||||
[app.main.data.workspace.fix-deleted-fonts :as fdf]
|
||||
[app.main.data.workspace.groups :as dwg]
|
||||
[app.main.data.workspace.guides :as dwgu]
|
||||
[app.main.data.workspace.highlight :as dwh]
|
||||
@@ -129,6 +130,7 @@
|
||||
components-v2 (features/active-feature? state :components-v2)]
|
||||
(rx/merge
|
||||
(rx/of (fbc/fix-bool-contents))
|
||||
(rx/of (fdf/fix-deleted-fonts))
|
||||
(if (and has-graphics? components-v2)
|
||||
(rx/of (remove-graphics (:id file) (:name file)))
|
||||
(rx/empty)))))))
|
||||
@@ -232,13 +234,15 @@
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file-data (:workspace-data state)
|
||||
(let [file-id (dm/get-in state [:workspace-file :id])
|
||||
ignore-until (dm/get-in state [:workspace-file :ignore-sync-until])
|
||||
file-id (dm/get-in state [:workspace-file :id])
|
||||
needs-update? (seq (filter #(dwl/assets-need-sync % file-data ignore-until)
|
||||
libraries))]
|
||||
(when needs-update?
|
||||
(rx/of (dwl/notify-sync-file file-id)))))))
|
||||
needs-check? (some #(and (> (:modified-at %) (:synced-at %))
|
||||
(or (not ignore-until)
|
||||
(> (:modified-at %) ignore-until)))
|
||||
libraries)]
|
||||
(when needs-check?
|
||||
(rx/concat (rx/timer 1000)
|
||||
(rx/of (dwl/notify-sync-file file-id))))))))
|
||||
|
||||
(defn- fetch-thumbnail-blob-uri
|
||||
[uri]
|
||||
@@ -1373,17 +1377,18 @@
|
||||
(not (contains? #{:group :bool} (:type head))))
|
||||
|
||||
no-bool-shapes? (->> all-selected (some (comp #{:frame :text} :type)))]
|
||||
|
||||
(rx/concat
|
||||
(when (and (some? shape) (not (contains? selected (:id shape))))
|
||||
(rx/of (dws/select-shape (:id shape))))
|
||||
(rx/of (show-context-menu
|
||||
(-> params
|
||||
(assoc
|
||||
:kind :shape
|
||||
:disable-booleans? (or no-bool-shapes? not-group-like?)
|
||||
:disable-flatten? no-bool-shapes?
|
||||
:selected (conj selected (:id shape)))))))))))
|
||||
|
||||
(if (and (some? shape) (not (contains? selected (:id shape))))
|
||||
(rx/concat
|
||||
(rx/of (dws/select-shape (:id shape)))
|
||||
(rx/of (show-shape-context-menu params)))
|
||||
(rx/of (show-context-menu
|
||||
(-> params
|
||||
(assoc
|
||||
:kind :shape
|
||||
:disable-booleans? (or no-bool-shapes? not-group-like?)
|
||||
:disable-flatten? no-bool-shapes?
|
||||
:selected (conj selected (:id shape)))))))))))
|
||||
|
||||
(defn show-page-item-context-menu
|
||||
[{:keys [position page] :as params}]
|
||||
|
||||
@@ -195,6 +195,7 @@
|
||||
(update [_ state]
|
||||
(log/info :msg "commit-changes"
|
||||
:js/undo-group (str undo-group)
|
||||
:js/file-id (str (or file-id "nil"))
|
||||
:js/redo-changes redo-changes
|
||||
:js/undo-changes undo-changes)
|
||||
(let [current-file-id (get state :current-file-id)
|
||||
|
||||
129
frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs
Normal file
129
frontend/src/app/main/data/workspace/fix_deleted_fonts.cljs
Normal file
@@ -0,0 +1,129 @@
|
||||
;; 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
|
||||
|
||||
(ns app.main.data.workspace.fix-deleted-fonts
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.text :as txt]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.fonts :as fonts]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; This event will update the file so the texts with non existing custom fonts try to be fixed.
|
||||
;; This can happen when:
|
||||
;; - Exporting/importing files to different teams or penpot instances
|
||||
;; - Moving files from one team to another in the same instance
|
||||
;; - Custom fonts are explicitly deleted in the team area
|
||||
|
||||
(defn has-invalid-font-family
|
||||
[node]
|
||||
(let [fonts (deref fonts/fontsdb)]
|
||||
(and
|
||||
(some? (:font-family node))
|
||||
(nil? (get fonts (:font-id node))))))
|
||||
|
||||
(defn calculate-alternative-font-id
|
||||
[value]
|
||||
(let [fonts (deref fonts/fontsdb)]
|
||||
(->> (vals fonts)
|
||||
(filter #(= (:family %) value))
|
||||
(first)
|
||||
:id)))
|
||||
|
||||
(defn should-fix-deleted-font-shape?
|
||||
[shape]
|
||||
(let [text-nodes (txt/node-seq txt/is-text-node? (:content shape))]
|
||||
(and (cph/text-shape? shape) (some has-invalid-font-family text-nodes))))
|
||||
|
||||
(defn should-fix-deleted-font-component?
|
||||
[component]
|
||||
(->> (:objects component)
|
||||
(vals)
|
||||
(d/seek should-fix-deleted-font-shape?)))
|
||||
|
||||
(defn should-fix-deleted-font-typography?
|
||||
[typography]
|
||||
(let [fonts (deref fonts/fontsdb)]
|
||||
(nil? (get fonts (:font-id typography)))))
|
||||
|
||||
(defn fix-deleted-font
|
||||
[node]
|
||||
(let [alternative-font-id (calculate-alternative-font-id (:font-family node))]
|
||||
(cond-> node
|
||||
(some? alternative-font-id) (assoc :font-id alternative-font-id))))
|
||||
|
||||
(defn fix-deleted-font-shape
|
||||
[shape]
|
||||
(let [transform (partial txt/transform-nodes has-invalid-font-family fix-deleted-font)]
|
||||
(update shape :content transform)))
|
||||
|
||||
(defn fix-deleted-font-component
|
||||
[component]
|
||||
(update component
|
||||
:objects
|
||||
(fn [objects]
|
||||
(d/mapm #(fix-deleted-font-shape %2) objects))))
|
||||
|
||||
(defn fix-deleted-font-typography
|
||||
[typography]
|
||||
(let [alternative-font-id (calculate-alternative-font-id (:font-family typography))]
|
||||
(cond-> typography
|
||||
(some? alternative-font-id) (assoc :font-id alternative-font-id))))
|
||||
|
||||
(defn fix-deleted-fonts
|
||||
[]
|
||||
(ptk/reify ::fix-deleted-fonts
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
|
||||
ids (into #{}
|
||||
(comp (filter should-fix-deleted-font-shape?) (map :id))
|
||||
(vals objects))
|
||||
|
||||
components (->> (wsh/lookup-local-components state)
|
||||
(vals)
|
||||
(filter should-fix-deleted-font-component?))
|
||||
|
||||
component-changes
|
||||
(into []
|
||||
(map (fn [component]
|
||||
{:type :mod-component
|
||||
:id (:id component)
|
||||
:objects (-> (fix-deleted-font-component component) :objects)}))
|
||||
components)
|
||||
|
||||
typographies (->> (get-in state [:workspace-data :typographies])
|
||||
(vals)
|
||||
(filter should-fix-deleted-font-typography?))
|
||||
|
||||
typography-changes
|
||||
(into []
|
||||
(map (fn [typography]
|
||||
{:type :mod-typography
|
||||
:typography (fix-deleted-font-typography typography)}))
|
||||
typographies)]
|
||||
|
||||
(rx/concat
|
||||
(rx/of (dch/update-shapes ids #(fix-deleted-font-shape %) {:reg-objects? false
|
||||
:save-undo? false
|
||||
:ignore-tree true}))
|
||||
(if (empty? component-changes)
|
||||
(rx/empty)
|
||||
(rx/of (dch/commit-changes {:origin it
|
||||
:redo-changes component-changes
|
||||
:undo-changes []
|
||||
:save-undo? false})))
|
||||
|
||||
(if (empty? typography-changes)
|
||||
(rx/empty)
|
||||
(rx/of (dch/commit-changes {:origin it
|
||||
:redo-changes typography-changes
|
||||
:undo-changes []
|
||||
:save-undo? false}))))))))
|
||||
@@ -12,10 +12,14 @@
|
||||
[app.common.pages.changes-builder :as pcb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.workspace.changes :as dch]
|
||||
[app.main.data.workspace.selection :as dws]
|
||||
[app.main.data.workspace.shapes :as dwsh]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[beicon.core :as rx]
|
||||
@@ -140,7 +144,7 @@
|
||||
(mapv #(get objects %)))
|
||||
parent-id (cph/get-parent-id objects (:id frame))
|
||||
idx-in-parent (->> (:id frame)
|
||||
(cph/get-position-on-parent objects)
|
||||
(cph/get-position-on-parent objects)
|
||||
inc)]
|
||||
|
||||
(-> (pcb/empty-changes it page-id)
|
||||
@@ -150,6 +154,46 @@
|
||||
(pcb/change-parent parent-id children idx-in-parent)
|
||||
(pcb/remove-objects [(:id frame)]))))
|
||||
|
||||
|
||||
(defn- clone-component-shapes-changes
|
||||
[changes shape objects]
|
||||
(let [shape-parent-id (:parent-id shape)
|
||||
new-shape-id (uuid/next)
|
||||
[_ new-shapes _]
|
||||
(ctst/clone-object shape
|
||||
shape-parent-id
|
||||
objects
|
||||
(fn [object _]
|
||||
(cond-> object
|
||||
(= new-shape-id (:parent-id object))
|
||||
(assoc :parent-id shape-parent-id)))
|
||||
(fn [object _] object)
|
||||
new-shape-id
|
||||
false)
|
||||
|
||||
new-shapes (->> new-shapes
|
||||
(filter #(not= (:id %) new-shape-id)))]
|
||||
(reduce
|
||||
(fn [changes shape]
|
||||
(pcb/add-object changes shape))
|
||||
changes
|
||||
new-shapes)))
|
||||
|
||||
(defn remove-component-changes
|
||||
[it page-id shape objects file-data file]
|
||||
(let [page (ctpl/get-page file-data page-id)
|
||||
components-v2 (dm/get-in file-data [:options :components-v2])
|
||||
;; In order to ungroup a component, we first make a clone of its shapes,
|
||||
;; and then we delete it
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects)
|
||||
(pcb/with-library-data file-data)
|
||||
(pcb/with-page page)
|
||||
(clone-component-shapes-changes shape objects)
|
||||
(dwsh/delete-shapes-changes file page objects [(:id shape)] it components-v2))]
|
||||
;; TODO: Should we call detach-comment-thread ?
|
||||
changes))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; GROUPS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -173,13 +217,18 @@
|
||||
(ptk/reify ::ungroup-selected
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
(let [page-id (:current-page-id state)
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
file-data (get state :workspace-data)
|
||||
file (wsh/get-local-file state)
|
||||
|
||||
prepare
|
||||
(fn [shape-id]
|
||||
(let [shape (get objects shape-id)]
|
||||
(cond
|
||||
(ctk/main-instance? shape)
|
||||
(remove-component-changes it page-id shape objects file-data file)
|
||||
|
||||
(or (cph/group-shape? shape) (cph/bool-shape? shape))
|
||||
(remove-group-changes it page-id shape objects)
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
[potok.core :as ptk]))
|
||||
|
||||
;; Change this to :info :debug or :trace to debug this module, or :warn to reset to default
|
||||
(log/set-level! :warn)
|
||||
(log/set-level! :debug)
|
||||
|
||||
(defn- log-changes
|
||||
[changes file]
|
||||
@@ -317,6 +317,26 @@
|
||||
components-v2 (features/active-feature? state :components-v2)]
|
||||
(rx/of (add-component2 selected components-v2))))))
|
||||
|
||||
|
||||
(defn add-multiple-components
|
||||
"Add several new components to current file library, from the currently selected shapes."
|
||||
[]
|
||||
(ptk/reify ::add-multiple-components
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [components-v2 (features/active-feature? state :components-v2)
|
||||
objects (wsh/lookup-page-objects state)
|
||||
selected (->> (wsh/lookup-selected state)
|
||||
(cph/clean-loops objects))
|
||||
added-components (map
|
||||
#(add-component2 [%] components-v2)
|
||||
selected)
|
||||
undo-id (js/Symbol)]
|
||||
(rx/concat
|
||||
(rx/of (dwu/start-undo-transaction undo-id))
|
||||
(rx/from added-components)
|
||||
(rx/of (dwu/commit-undo-transaction undo-id)))))))
|
||||
|
||||
(defn rename-component
|
||||
"Rename the component with the given id, in the current file library."
|
||||
[id new-name]
|
||||
@@ -433,9 +453,9 @@
|
||||
(ptk/reify ::restore-component
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page-id (:current-page-id state)
|
||||
current-page (dm/get-in state [:workspace-data :pages-index page-id])
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
(let [page-id (:current-page-id state)
|
||||
current-page (dm/get-in state [:workspace-data :pages-index page-id])
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
library-data (wsh/get-file state library-id)
|
||||
{:keys [changes shape]} (dwlh/prepare-restore-component library-data component-id current-page it)
|
||||
parent-id (:parent-id shape)
|
||||
@@ -444,14 +464,13 @@
|
||||
(update-in [parent-id :shapes]
|
||||
#(conj % (:id shape))))
|
||||
|
||||
|
||||
;; Adds a resize-parents operation so the groups are updated. We add all the new objects
|
||||
new-objects-ids (->> changes :redo-changes (filter #(= (:type %) :add-obj)) (mapv :id))
|
||||
changes (-> changes
|
||||
(pcb/with-objects objects)
|
||||
(pcb/resize-parents new-objects-ids))]
|
||||
(rx/of (dch/commit-changes changes))))))
|
||||
|
||||
(rx/of (dch/commit-changes (assoc changes :file-id library-id)))))))
|
||||
|
||||
(defn instantiate-component
|
||||
"Create a new shape in the current page, from the component with the given id
|
||||
@@ -460,14 +479,14 @@
|
||||
(dm/assert! (uuid? file-id))
|
||||
(dm/assert! (uuid? component-id))
|
||||
(dm/assert! (gpt/point? position))
|
||||
|
||||
(ptk/reify ::instantiate-component
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [page (wsh/lookup-page state)
|
||||
libraries (wsh/get-libraries state)
|
||||
|
||||
changes (pcb/empty-changes it (:id page))
|
||||
changes (-> (pcb/empty-changes it (:id page))
|
||||
(pcb/with-objects (:objects page)))
|
||||
|
||||
[new-shape changes]
|
||||
(dwlh/generate-instantiate-component changes
|
||||
@@ -842,9 +861,9 @@
|
||||
The sequence items are tuples of (page-id shape-id asset-id asset-type)."
|
||||
([library file-data] (assets-need-sync library file-data nil))
|
||||
([library file-data ignore-until]
|
||||
(let [sync-date (max (:synced-at library) (or ignore-until 0))]
|
||||
(when (> (:modified-at library) sync-date)
|
||||
(ctf/used-assets-changed-since file-data library sync-date)))))
|
||||
(let [sync-date (max (:synced-at library) (or ignore-until 0))]
|
||||
(when (> (:modified-at library) sync-date)
|
||||
(ctf/used-assets-changed-since file-data library sync-date)))))
|
||||
|
||||
(defn notify-sync-file
|
||||
[file-id]
|
||||
@@ -853,7 +872,8 @@
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [file-data (:workspace-data state)
|
||||
libraries-need-sync (filter #(seq (assets-need-sync % file-data))
|
||||
ignore-until (dm/get-in state [:workspace-file :ignore-sync-until])
|
||||
libraries-need-sync (filter #(seq (assets-need-sync % file-data ignore-until))
|
||||
(vals (get state :workspace-libraries)))
|
||||
do-update #(do (apply st/emit! (map (fn [library]
|
||||
(sync-file (:current-file-id state)
|
||||
@@ -904,10 +924,15 @@
|
||||
check-changes
|
||||
(fn [[event [old-data _mid_data _new-data]]]
|
||||
(when old-data
|
||||
(let [{:keys [changes save-undo? undo-group]} (deref event)
|
||||
components-changed (reduce #(into %1 (ch/components-changed old-data %2))
|
||||
#{}
|
||||
changes)]
|
||||
(let [{:keys [file-id changes save-undo? undo-group]}
|
||||
(deref event)
|
||||
|
||||
components-changed
|
||||
(when (or (nil? file-id) (= file-id (:id old-data)))
|
||||
(reduce #(into %1 (ch/components-changed old-data %2))
|
||||
#{}
|
||||
changes))]
|
||||
|
||||
(when (and (d/not-empty? components-changed) save-undo?)
|
||||
(log/info :msg "DETECTED COMPONENTS CHANGED"
|
||||
:ids (map str components-changed)
|
||||
|
||||
@@ -181,12 +181,14 @@
|
||||
(not (nil? parent-id))
|
||||
(assoc :parent-id parent-id))
|
||||
|
||||
changes (cond-> (pcb/add-object changes first-shape {:ignore-touched true})
|
||||
(some? old-id) (pcb/amend-last-change #(assoc % :old-id old-id))) ; on copy/paste old id is used later to reorder the paster layers
|
||||
|
||||
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
|
||||
changes
|
||||
(rest new-shapes))]
|
||||
changes (as-> changes $
|
||||
(pcb/add-object $ first-shape {:ignore-touched true})
|
||||
(cond-> $
|
||||
(some? old-id)
|
||||
(pcb/amend-last-change #(assoc % :old-id old-id))) ; on copy/paste old id is used later to reorder the paster layers
|
||||
(reduce #(pcb/add-object %1 %2 {:ignore-touched true})
|
||||
$
|
||||
(rest new-shapes)))]
|
||||
|
||||
[new-shape changes])))
|
||||
|
||||
@@ -228,6 +230,7 @@
|
||||
(assoc :parent-id parent-id))
|
||||
changes (-> (or changes (pcb/empty-changes it))
|
||||
(pcb/with-page page)
|
||||
(pcb/with-objects (:objects page))
|
||||
(pcb/with-library-data library-data))
|
||||
changes (cond-> (pcb/add-object changes first-shape {:ignore-touched true})
|
||||
(some? old-id) (pcb/amend-last-change #(assoc % :old-id old-id))) ; on copy/paste old id is used later to reorder the paster layers
|
||||
|
||||
@@ -428,11 +428,12 @@
|
||||
:frame-id frame-id)
|
||||
(dissoc :shapes
|
||||
:main-instance?
|
||||
:shape-ref
|
||||
:use-for-thumbnail?)
|
||||
(gsh/move delta)
|
||||
(d/update-when :interactions #(ctsi/remap-interactions % ids-map objects)))
|
||||
|
||||
changes (-> (pcb/add-object changes new-obj {:ignore-touched true})
|
||||
changes (-> (pcb/add-object changes new-obj)
|
||||
(pcb/amend-last-change #(assoc % :old-id (:id obj))))
|
||||
|
||||
changes (cond-> changes
|
||||
|
||||
@@ -108,7 +108,8 @@
|
||||
objects (wsh/lookup-page-objects state page-id)
|
||||
selected (wsh/lookup-selected state)
|
||||
|
||||
changes (pcb/empty-changes it page-id)
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects))
|
||||
|
||||
[shape changes]
|
||||
(prepare-add-shape changes attrs objects selected)
|
||||
@@ -214,126 +215,140 @@
|
||||
(real-delete-shapes file page objects ids-to-delete it components-v2)
|
||||
(rx/of (dwu/commit-undo-transaction undo-id))))))))
|
||||
|
||||
(defn- real-delete-shapes
|
||||
[file page objects ids it components-v2]
|
||||
(let [lookup (d/getf objects)
|
||||
|
||||
groups-to-unmask
|
||||
(reduce (fn [group-ids id]
|
||||
(defn- real-delete-shapes-changes
|
||||
([file page objects ids it components-v2]
|
||||
(let [changes (-> (pcb/empty-changes it (:id page))
|
||||
(pcb/with-page page)
|
||||
(pcb/with-objects objects)
|
||||
(pcb/with-library-data file))]
|
||||
(real-delete-shapes-changes changes file page objects ids it components-v2)))
|
||||
([changes file page objects ids _it components-v2]
|
||||
(let [lookup (d/getf objects)
|
||||
groups-to-unmask
|
||||
(reduce (fn [group-ids id]
|
||||
;; When the shape to delete is the mask of a masked group,
|
||||
;; the mask condition must be removed, and it must be
|
||||
;; converted to a normal group.
|
||||
(let [obj (lookup id)
|
||||
parent (lookup (:parent-id obj))]
|
||||
(if (and (:masked-group? parent)
|
||||
(= id (first (:shapes parent))))
|
||||
(conj group-ids (:id parent))
|
||||
group-ids)))
|
||||
#{}
|
||||
ids)
|
||||
(let [obj (lookup id)
|
||||
parent (lookup (:parent-id obj))]
|
||||
(if (and (:masked-group? parent)
|
||||
(= id (first (:shapes parent))))
|
||||
(conj group-ids (:id parent))
|
||||
group-ids)))
|
||||
#{}
|
||||
ids)
|
||||
|
||||
interacting-shapes
|
||||
(filter (fn [shape]
|
||||
interacting-shapes
|
||||
(filter (fn [shape]
|
||||
;; If any of the deleted shapes is the destination of
|
||||
;; some interaction, this must be deleted, too.
|
||||
(let [interactions (:interactions shape)]
|
||||
(some #(and (ctsi/has-destination %)
|
||||
(contains? ids (:destination %)))
|
||||
interactions)))
|
||||
(vals objects))
|
||||
(let [interactions (:interactions shape)]
|
||||
(some #(and (ctsi/has-destination %)
|
||||
(contains? ids (:destination %)))
|
||||
interactions)))
|
||||
(vals objects))
|
||||
|
||||
;; If any of the deleted shapes is a frame with guides
|
||||
guides (into {}
|
||||
(comp (map second)
|
||||
(remove #(contains? ids (:frame-id %)))
|
||||
(map (juxt :id identity)))
|
||||
(dm/get-in page [:options :guides]))
|
||||
guides (into {}
|
||||
(comp (map second)
|
||||
(remove #(contains? ids (:frame-id %)))
|
||||
(map (juxt :id identity)))
|
||||
(dm/get-in page [:options :guides]))
|
||||
|
||||
starting-flows
|
||||
(filter (fn [flow]
|
||||
starting-flows
|
||||
(filter (fn [flow]
|
||||
;; If any of the deleted is a frame that starts a flow,
|
||||
;; this must be deleted, too.
|
||||
(contains? ids (:starting-frame flow)))
|
||||
(-> page :options :flows))
|
||||
(contains? ids (:starting-frame flow)))
|
||||
(-> page :options :flows))
|
||||
|
||||
all-parents
|
||||
(reduce (fn [res id]
|
||||
all-parents
|
||||
(reduce (fn [res id]
|
||||
;; All parents of any deleted shape must be resized.
|
||||
(into res (cph/get-parent-ids objects id)))
|
||||
(d/ordered-set)
|
||||
ids)
|
||||
(into res (cph/get-parent-ids objects id)))
|
||||
(d/ordered-set)
|
||||
ids)
|
||||
|
||||
all-children
|
||||
(->> ids ;; Children of deleted shapes must be also deleted.
|
||||
(reduce (fn [res id]
|
||||
(into res (cph/get-children-ids objects id)))
|
||||
[])
|
||||
(reverse)
|
||||
(into (d/ordered-set)))
|
||||
all-children
|
||||
(->> ids ;; Children of deleted shapes must be also deleted.
|
||||
(reduce (fn [res id]
|
||||
(into res (cph/get-children-ids objects id)))
|
||||
[])
|
||||
(reverse)
|
||||
(into (d/ordered-set)))
|
||||
|
||||
find-all-empty-parents
|
||||
(fn recursive-find-empty-parents [empty-parents]
|
||||
(let [all-ids (into empty-parents ids)
|
||||
contains? (partial contains? all-ids)
|
||||
xform (comp (map lookup)
|
||||
(filter cph/group-shape?)
|
||||
(remove #(->> (:shapes %) (remove contains?) seq))
|
||||
(map :id))
|
||||
parents (into #{} xform all-parents)]
|
||||
(if (= empty-parents parents)
|
||||
empty-parents
|
||||
(recursive-find-empty-parents parents))))
|
||||
find-all-empty-parents
|
||||
(fn recursive-find-empty-parents [empty-parents]
|
||||
(let [all-ids (into empty-parents ids)
|
||||
contains? (partial contains? all-ids)
|
||||
xform (comp (map lookup)
|
||||
(filter cph/group-shape?)
|
||||
(remove #(->> (:shapes %) (remove contains?) seq))
|
||||
(map :id))
|
||||
parents (into #{} xform all-parents)]
|
||||
(if (= empty-parents parents)
|
||||
empty-parents
|
||||
(recursive-find-empty-parents parents))))
|
||||
|
||||
empty-parents
|
||||
empty-parents
|
||||
;; Any parent whose children are all deleted, must be deleted too.
|
||||
(into (d/ordered-set) (find-all-empty-parents #{}))
|
||||
(into (d/ordered-set) (find-all-empty-parents #{}))
|
||||
|
||||
components-to-delete
|
||||
(if components-v2
|
||||
(reduce (fn [components id]
|
||||
(let [shape (get objects id)]
|
||||
(if (and (= (:component-file shape) (:id file)) ;; Main instances should exist only in local file
|
||||
(:main-instance? shape)) ;; but check anyway
|
||||
(conj components (:component-id shape))
|
||||
components)))
|
||||
[]
|
||||
(into ids all-children))
|
||||
[])
|
||||
components-to-delete
|
||||
(if components-v2
|
||||
(reduce (fn [components id]
|
||||
(let [shape (get objects id)]
|
||||
(if (and (= (:component-file shape) (:id file)) ;; Main instances should exist only in local file
|
||||
(:main-instance? shape)) ;; but check anyway
|
||||
(conj components (:component-id shape))
|
||||
components)))
|
||||
[]
|
||||
(into ids all-children))
|
||||
[])
|
||||
|
||||
changes (-> (pcb/empty-changes it (:id page))
|
||||
(pcb/with-page page)
|
||||
(pcb/with-objects objects)
|
||||
(pcb/with-library-data file)
|
||||
(pcb/set-page-option :guides guides))
|
||||
changes (-> changes
|
||||
(pcb/set-page-option :guides guides))
|
||||
|
||||
changes (reduce (fn [changes component-id]
|
||||
changes (reduce (fn [changes component-id]
|
||||
;; It's important to delete the component before the main instance, because we
|
||||
;; need to store the instance position if we want to restore it later.
|
||||
(pcb/delete-component changes component-id))
|
||||
changes
|
||||
components-to-delete)
|
||||
(pcb/delete-component changes component-id))
|
||||
changes
|
||||
components-to-delete)
|
||||
|
||||
changes (-> changes
|
||||
(pcb/remove-objects all-children {:ignore-touched true})
|
||||
(pcb/remove-objects ids)
|
||||
(pcb/remove-objects empty-parents)
|
||||
(pcb/resize-parents all-parents)
|
||||
(pcb/update-shapes groups-to-unmask
|
||||
(fn [shape]
|
||||
(assoc shape :masked-group? false)))
|
||||
(pcb/update-shapes (map :id interacting-shapes)
|
||||
(fn [shape]
|
||||
(d/update-when shape :interactions
|
||||
(fn [interactions]
|
||||
(into []
|
||||
(remove #(and (ctsi/has-destination %)
|
||||
(contains? ids (:destination %))))
|
||||
interactions)))))
|
||||
(cond-> (seq starting-flows)
|
||||
(pcb/update-page-option :flows (fn [flows]
|
||||
(->> (map :id starting-flows)
|
||||
(reduce ctp/remove-flow flows))))))
|
||||
undo-id (js/Symbol)]
|
||||
changes (-> changes
|
||||
(pcb/remove-objects all-children {:ignore-touched true})
|
||||
(pcb/remove-objects ids)
|
||||
(pcb/remove-objects empty-parents)
|
||||
(pcb/resize-parents all-parents)
|
||||
(pcb/update-shapes groups-to-unmask
|
||||
(fn [shape]
|
||||
(assoc shape :masked-group? false)))
|
||||
(pcb/update-shapes (map :id interacting-shapes)
|
||||
(fn [shape]
|
||||
(d/update-when shape :interactions
|
||||
(fn [interactions]
|
||||
(into []
|
||||
(remove #(and (ctsi/has-destination %)
|
||||
(contains? ids (:destination %))))
|
||||
interactions)))))
|
||||
(cond-> (seq starting-flows)
|
||||
(pcb/update-page-option :flows (fn [flows]
|
||||
(->> (map :id starting-flows)
|
||||
(reduce ctp/remove-flow flows))))))]
|
||||
[changes all-parents])))
|
||||
|
||||
|
||||
(defn delete-shapes-changes
|
||||
[changes file page objects ids it components-v2]
|
||||
(let [[changes _all-parents] (real-delete-shapes-changes changes file page objects ids it components-v2)]
|
||||
changes))
|
||||
|
||||
|
||||
(defn- real-delete-shapes
|
||||
[file page objects ids it components-v2]
|
||||
(let [[changes all-parents] (real-delete-shapes-changes file page objects ids it components-v2)
|
||||
undo-id (js/Symbol)]
|
||||
(rx/of (dwu/start-undo-transaction undo-id)
|
||||
(dc/detach-comment-thread ids)
|
||||
(dch/commit-changes changes)
|
||||
@@ -419,7 +434,8 @@
|
||||
selected (wsh/lookup-selected state)
|
||||
selected (cph/clean-loops objects selected)
|
||||
|
||||
changes (pcb/empty-changes it page-id)
|
||||
changes (-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects objects))
|
||||
|
||||
[frame-shape changes]
|
||||
(prepare-create-artboard-from-selection changes
|
||||
@@ -467,7 +483,7 @@
|
||||
;; We have change only the hidden behaviour, to hide only the
|
||||
;; selected shape, block behaviour remains the same.
|
||||
ids (if (boolean? blocked)
|
||||
(into ids (->> ids (mapcat #(cph/get-children-ids objects %))))
|
||||
(into ids (->> ids (mapcat #(cph/get-children-ids objects %))))
|
||||
ids)]
|
||||
(rx/of (dch/update-shapes ids update-fn))))))
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.file :as ctf]
|
||||
[app.common.types.modifiers :as ctm]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.config :as cfg]
|
||||
@@ -378,18 +379,20 @@
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(mf/defc component-symbol
|
||||
[{:keys [id data] :as props}]
|
||||
(let [name (:name data)
|
||||
path (:path data)
|
||||
objects (-> (:objects data)
|
||||
(adapt-objects-for-shape id))
|
||||
root-shape (get objects id)
|
||||
[{:keys [component] :as props}]
|
||||
(let [name (:name component)
|
||||
path (:path component)
|
||||
root-id (or (:main-instance-id component)
|
||||
(:id component))
|
||||
objects (adapt-objects-for-shape (:objects component)
|
||||
root-id)
|
||||
root-shape (get objects root-id)
|
||||
selrect (:selrect root-shape)
|
||||
|
||||
main-instance-id (:main-instance-id data)
|
||||
main-instance-page (:main-instance-page data)
|
||||
main-instance-x (:main-instance-x data)
|
||||
main-instance-y (:main-instance-y data)
|
||||
main-instance-id (:main-instance-id component)
|
||||
main-instance-page (:main-instance-page component)
|
||||
main-instance-x (:main-instance-x component)
|
||||
main-instance-y (:main-instance-y component)
|
||||
|
||||
vbox
|
||||
(format-viewbox
|
||||
@@ -406,7 +409,7 @@
|
||||
(mf/deps objects)
|
||||
(fn [] (frame-wrapper-factory objects)))]
|
||||
|
||||
[:> "symbol" #js {:id (str id)
|
||||
[:> "symbol" #js {:id (str root-id)
|
||||
:viewBox vbox
|
||||
"penpot:path" path
|
||||
"penpot:main-instance-id" main-instance-id
|
||||
@@ -436,9 +439,10 @@
|
||||
:style {:display (when-not (some? children) "none")}
|
||||
:fill "none"}
|
||||
[:defs
|
||||
(for [[id data] (source data)]
|
||||
[:& component-symbol {:id id :key (dm/str id) :data data}])]
|
||||
|
||||
(for [[id component] (source data)]
|
||||
(let [component (ctf/load-component-objects data component)]
|
||||
[:& component-symbol {:key (dm/str id) :component component}]))]
|
||||
|
||||
children]]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
@@ -498,6 +502,7 @@
|
||||
(let [;; Join all components objects into a single map
|
||||
objects (->> (source data)
|
||||
(vals)
|
||||
(map (partial ctf/load-component-objects data))
|
||||
(map :objects)
|
||||
(reduce conj))]
|
||||
(rx/concat
|
||||
|
||||
@@ -106,7 +106,7 @@
|
||||
|
||||
:viewer
|
||||
(let [{:keys [query-params path-params]} route
|
||||
{:keys [index share-id section page-id] :or {section :interactions}} query-params
|
||||
{:keys [index share-id section page-id interactions-mode] :or {section :interactions interactions-mode :show-on-click}} query-params
|
||||
{:keys [file-id]} path-params]
|
||||
(if (:token query-params)
|
||||
[:& viewer/breaking-change-notice]
|
||||
@@ -114,7 +114,12 @@
|
||||
:file-id file-id
|
||||
:section section
|
||||
:index index
|
||||
:share-id share-id}]))
|
||||
:share-id share-id
|
||||
:interactions-mode (keyword interactions-mode)
|
||||
:interactions-show? (case (keyword interactions-mode)
|
||||
:hide false
|
||||
:show true
|
||||
:show-on-click false)}]))
|
||||
|
||||
:workspace
|
||||
(let [project-id (some-> params :path :project-id uuid)
|
||||
|
||||
@@ -43,7 +43,6 @@
|
||||
(mf/use-fn
|
||||
(mf/deps on-change)
|
||||
(fn [event]
|
||||
(js/console.log event)
|
||||
(let [value (-> (dom/get-current-target event)
|
||||
(dom/get-data "value")
|
||||
(d/read-string))]
|
||||
|
||||
70
frontend/src/app/main/ui/css_cursors.cljs
Normal file
70
frontend/src/app/main/ui/css_cursors.cljs
Normal file
@@ -0,0 +1,70 @@
|
||||
(ns app.main.ui.css-cursors
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.util.css :as css]))
|
||||
|
||||
;; angle per rotation
|
||||
(def angle-step 10)
|
||||
|
||||
(defn get-static
|
||||
"Returns the class name of a static cursor"
|
||||
[name]
|
||||
(dm/str "cursor-" name))
|
||||
|
||||
(defn get-dynamic
|
||||
"Returns the class name of a dynamic cursor (with rotation)"
|
||||
[name rotation]
|
||||
(dm/str "cursor-" name "-" (mod (* (.floor js/Math (/ rotation angle-step)) angle-step) 360)))
|
||||
|
||||
(defn init-static-cursor-style
|
||||
"Initializes a static cursor style"
|
||||
[style name value]
|
||||
(.add style (dm/str ".cursor-" name) (js-obj "cursor" (dm/str value " !important"))))
|
||||
|
||||
(defn init-dynamic-cursor-style
|
||||
"Initializes a dynamic cursor style"
|
||||
[style name fn]
|
||||
(let [rotations (seq (range 0 360 angle-step))]
|
||||
(doseq [rotation rotations]
|
||||
(.add style (dm/str ".cursor-" name "-" rotation) (js-obj "cursor" (dm/str (fn rotation) " !important"))))))
|
||||
|
||||
(defn init-styles
|
||||
"Initializes all cursor styles"
|
||||
[]
|
||||
(let [style (css/create-style "css-cursors")]
|
||||
;; static
|
||||
(init-static-cursor-style style "comments" cur/comments)
|
||||
(init-static-cursor-style style "create-artboard" cur/create-artboard)
|
||||
(init-static-cursor-style style "create-ellipse" cur/create-ellipse)
|
||||
(init-static-cursor-style style "create-polygon" cur/create-polygon)
|
||||
(init-static-cursor-style style "create-rectangle" cur/create-rectangle)
|
||||
(init-static-cursor-style style "create-shape" cur/create-shape)
|
||||
(init-static-cursor-style style "duplicate" cur/duplicate)
|
||||
(init-static-cursor-style style "hand" cur/hand)
|
||||
(init-static-cursor-style style "move-pointer" cur/move-pointer)
|
||||
(init-static-cursor-style style "pen" cur/pen)
|
||||
(init-static-cursor-style style "pen-node" cur/pen-node)
|
||||
(init-static-cursor-style style "pencil" cur/pencil)
|
||||
(init-static-cursor-style style "picker" cur/picker)
|
||||
(init-static-cursor-style style "pointer-inner" cur/pointer-inner)
|
||||
(init-static-cursor-style style "pointer-move" cur/pointer-move)
|
||||
(init-static-cursor-style style "pointer-node" cur/pointer-node)
|
||||
(init-static-cursor-style style "resize-alt" cur/resize-alt)
|
||||
(init-static-cursor-style style "zoom" cur/zoom)
|
||||
(init-static-cursor-style style "zoom-in" cur/zoom-in)
|
||||
(init-static-cursor-style style "zoom-out" cur/zoom-out)
|
||||
|
||||
;; dynamic
|
||||
(init-dynamic-cursor-style style "resize-ew" cur/resize-ew)
|
||||
(init-dynamic-cursor-style style "resize-nesw" cur/resize-nesw)
|
||||
(init-dynamic-cursor-style style "resize-ns" cur/resize-ns)
|
||||
(init-dynamic-cursor-style style "resize-nwse" cur/resize-nwse)
|
||||
(init-dynamic-cursor-style style "rotate" cur/rotate)
|
||||
(init-dynamic-cursor-style style "text" cur/text)
|
||||
(init-dynamic-cursor-style style "scale-ew" cur/scale-ew)
|
||||
(init-dynamic-cursor-style style "scale-nesw" cur/scale-nesw)
|
||||
(init-dynamic-cursor-style style "scale-ns" cur/scale-ns)
|
||||
(init-dynamic-cursor-style style "scale-nwse" cur/scale-nwse)
|
||||
(init-dynamic-cursor-style style "resize-ew-2" cur/resize-ew-2)
|
||||
(init-dynamic-cursor-style style "resize-ns-2" cur/resize-ns-2)))
|
||||
@@ -7,8 +7,6 @@
|
||||
(ns app.main.ui.cursors
|
||||
(:require-macros [app.main.ui.cursors :refer [cursor-ref cursor-fn]])
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.util.css :as css]
|
||||
[app.util.timers :as ts]
|
||||
[cuerdas.core :as str]
|
||||
[rumext.v2 :as mf]))
|
||||
@@ -43,7 +41,7 @@
|
||||
(def rotate (cursor-fn :rotate 90))
|
||||
(def text (cursor-fn :text 0))
|
||||
|
||||
;; text
|
||||
;; Text
|
||||
(def scale-ew (cursor-fn :scale-h 0))
|
||||
(def scale-nesw (cursor-fn :scale-h 45))
|
||||
(def scale-ns (cursor-fn :scale-h 90))
|
||||
@@ -52,63 +50,6 @@
|
||||
;;
|
||||
(def resize-ew-2 (cursor-fn :resize-h-2 0))
|
||||
(def resize-ns-2 (cursor-fn :resize-h-2 90))
|
||||
|
||||
(defn get-static
|
||||
[name]
|
||||
(dm/str "cursor-" name))
|
||||
|
||||
(defn get-dynamic
|
||||
[name rotation]
|
||||
(dm/str "cursor-" name "-" (.floor js/Math rotation)))
|
||||
|
||||
(defn init-static-cursor-style
|
||||
[style name value]
|
||||
(.add style (dm/str ".cursor-" name) (js-obj "cursor" (dm/str value " !important"))))
|
||||
|
||||
(defn init-dynamic-cursor-style
|
||||
[style name fn]
|
||||
(let [rotations (seq (range 0 360 1))]
|
||||
(doseq [rotation rotations]
|
||||
(.add style (dm/str ".cursor-" name "-" rotation) (js-obj "cursor" (dm/str (fn rotation) " !important"))))))
|
||||
|
||||
(defn init-styles
|
||||
[]
|
||||
(let [style (css/create-style)]
|
||||
;; static
|
||||
(init-static-cursor-style style "comments" comments)
|
||||
(init-static-cursor-style style "create-artboard" create-artboard)
|
||||
(init-static-cursor-style style "create-ellipse" create-ellipse)
|
||||
(init-static-cursor-style style "create-polygon" create-polygon)
|
||||
(init-static-cursor-style style "create-rectangle" create-rectangle)
|
||||
(init-static-cursor-style style "create-shape" create-shape)
|
||||
(init-static-cursor-style style "duplicate" duplicate)
|
||||
(init-static-cursor-style style "hand" hand)
|
||||
(init-static-cursor-style style "move-pointer" move-pointer)
|
||||
(init-static-cursor-style style "pen" pen)
|
||||
(init-static-cursor-style style "pen-node" pen-node)
|
||||
(init-static-cursor-style style "pencil" pencil)
|
||||
(init-static-cursor-style style "picker" picker)
|
||||
(init-static-cursor-style style "pointer-inner" pointer-inner)
|
||||
(init-static-cursor-style style "pointer-move" pointer-move)
|
||||
(init-static-cursor-style style "pointer-node" pointer-node)
|
||||
(init-static-cursor-style style "resize-alt" resize-alt)
|
||||
(init-static-cursor-style style "zoom" zoom)
|
||||
(init-static-cursor-style style "zoom-in" zoom-in)
|
||||
(init-static-cursor-style style "zoom-out" zoom-out)
|
||||
|
||||
;; dynamic
|
||||
(init-dynamic-cursor-style style "resize-ew" resize-ew)
|
||||
(init-dynamic-cursor-style style "resize-nesw" resize-nesw)
|
||||
(init-dynamic-cursor-style style "resize-ns" resize-ns)
|
||||
(init-dynamic-cursor-style style "resize-nwse" resize-nwse)
|
||||
(init-dynamic-cursor-style style "rotate" rotate)
|
||||
(init-dynamic-cursor-style style "text" text)
|
||||
(init-dynamic-cursor-style style "scale-ew" scale-ew)
|
||||
(init-dynamic-cursor-style style "scale-nesw" scale-nesw)
|
||||
(init-dynamic-cursor-style style "scale-ns" scale-ns)
|
||||
(init-dynamic-cursor-style style "scale-nwse" scale-nwse)
|
||||
(init-dynamic-cursor-style style "resize-ew-2" resize-ew-2)
|
||||
(init-dynamic-cursor-style style "resize-ns-2" resize-ns-2)))
|
||||
|
||||
(mf/defc debug-preview
|
||||
{::mf/wrap-props false}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]]
|
||||
[app.util.dom :as dom]
|
||||
@@ -516,9 +516,9 @@
|
||||
:on-pointer-down on-pointer-down
|
||||
:on-lost-pointer-capture on-lost-pointer-capture
|
||||
:on-pointer-move on-pointer-move
|
||||
:class (when (or hover? selected?)
|
||||
(if (= (:resize-axis rect-data) :x) (cur/get-dynamic "resize-ew" 0) (cur/get-dynamic "resize-ew" 90)))
|
||||
:style {:fill (if (or hover? selected?) warning-color "none")
|
||||
:cursor (when (or hover? selected?)
|
||||
(if (= (:resize-axis rect-data) :x) (cur/resize-ew 0) (cur/resize-ew 90)))
|
||||
:opacity (if selected? 0.5 0.25)}}]))
|
||||
|
||||
(mf/defc margin-rects [{:keys [shape frame zoom alt? shift?]}]
|
||||
|
||||
@@ -194,8 +194,13 @@
|
||||
(obj/set! "fill" (obj/get svg-attrs "fill"))
|
||||
(obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity")))
|
||||
|
||||
;; If contains svg-attrs the origin is svg. If it's not svg origin
|
||||
;; we setup the default fill as black
|
||||
;; If the shape comes from an imported SVG (we know because it has
|
||||
;; the :svg-attrs atribute), and it does not have an own fill, we
|
||||
;; set a default black fill. This will be inherited by child nodes,
|
||||
;; and is for emulating the behavior of standard SVG, in that a node
|
||||
;; that has no explicit fill has a default fill of black.
|
||||
;; This may be reset to normal if a Penpot frame shape appears below
|
||||
;; (see main.ui.shapes.frame/frame-container).
|
||||
(and (contains? shape :svg-attrs)
|
||||
(#{:svg-raw :group} (:type shape))
|
||||
(empty? (:fills shape)))
|
||||
|
||||
@@ -370,9 +370,7 @@
|
||||
(-> props
|
||||
(obj/set! "style" style)))
|
||||
|
||||
(and (some? svg-attrs)
|
||||
(obj/contains? svg-attrs "fill")
|
||||
(empty? (:fills shape)))
|
||||
(and (some? svg-attrs) (empty? (:fills shape)))
|
||||
(let [style
|
||||
(-> (obj/get child "props")
|
||||
(obj/get "style")
|
||||
@@ -407,7 +405,7 @@
|
||||
(obj/set! "style" style)))
|
||||
|
||||
:else
|
||||
nil)))
|
||||
(obj/create))))
|
||||
|
||||
(defn build-stroke-props [position child value render-id]
|
||||
(let [props (-> (obj/get child "props")
|
||||
@@ -430,11 +428,10 @@
|
||||
position (or (obj/get props "position") 0)
|
||||
render-id (or (obj/get props "render-id") (mf/use-ctx muc/render-id))
|
||||
fill-props (build-fill-props shape child position render-id)]
|
||||
(when (some? fill-props)
|
||||
[:g.fills {:id (dm/fmt "fills-%" (:id shape))}
|
||||
[:> elem-name (-> (obj/get child "props")
|
||||
(obj/clone)
|
||||
(obj/merge! fill-props))]])))
|
||||
[:g.fills {:id (dm/fmt "fills-%" (:id shape))}
|
||||
[:> elem-name (-> (obj/get child "props")
|
||||
(obj/clone)
|
||||
(obj/merge! fill-props))]]))
|
||||
|
||||
(mf/defc shape-strokes
|
||||
{::mf/wrap-props false}
|
||||
|
||||
@@ -69,7 +69,10 @@
|
||||
:className "frame-background"}))
|
||||
path? (some? (.-d props))]
|
||||
[:*
|
||||
[:g {:clip-path (when (not show-content) (frame-clip-url shape render-id))}
|
||||
[:g {:clip-path (when (not show-content) (frame-clip-url shape render-id))
|
||||
:fill "none"} ;; A frame sets back normal fill behavior (default transparent). It may have
|
||||
;; been changed to default black if a shape coming from an imported SVG file
|
||||
;; is rendered. See main.ui.shapes.attrs/add-style-attrs.
|
||||
[:& frame-clip-def {:shape shape :render-id render-id}]
|
||||
|
||||
[:& shape-fills {:shape shape}
|
||||
|
||||
@@ -158,7 +158,8 @@
|
||||
:index index
|
||||
:page page
|
||||
:users users
|
||||
:frame frame}]
|
||||
:frame frame
|
||||
:interactions-mode interactions-mode}]
|
||||
|
||||
[:div.viewer-wrapper
|
||||
{:style {:width (:width wrapper-size)
|
||||
@@ -178,7 +179,7 @@
|
||||
:size orig-size
|
||||
:page page
|
||||
:users users
|
||||
:interactions-mode :hide}]])
|
||||
:interactions-mode interactions-mode}]])
|
||||
|
||||
[:div.viewport-container
|
||||
{:ref current-viewport-ref
|
||||
@@ -214,7 +215,7 @@
|
||||
(mf/defc viewer
|
||||
[{:keys [params data]}]
|
||||
|
||||
(let [{:keys [page-id section index]} params
|
||||
(let [{:keys [page-id section index interactions-mode]} params
|
||||
{:keys [file users project permissions]} data
|
||||
|
||||
allowed (or
|
||||
@@ -281,9 +282,6 @@
|
||||
(mf/with-memo [size orig-size zoom]
|
||||
(calculate-wrapper size orig-size zoom))
|
||||
|
||||
interactions-mode
|
||||
(:interactions-mode local)
|
||||
|
||||
click-on-screen
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
@@ -484,7 +482,8 @@
|
||||
:frame frame
|
||||
:permissions permissions
|
||||
:zoom zoom
|
||||
:section section}]
|
||||
:section section
|
||||
:interactions-mode interactions-mode}]
|
||||
[:div.thumbnail-close {:on-click #(st/emit! dv/close-thumbnails-panel)
|
||||
:class (dom/classnames :invisible (not (:show-thumbnails local false)))}]
|
||||
[:& thumbnails-panel {:frames frames
|
||||
@@ -516,7 +515,8 @@
|
||||
:local local
|
||||
:size size
|
||||
:index index
|
||||
:viewer-pagination viewer-pagination}]
|
||||
:viewer-pagination viewer-pagination
|
||||
:interactions-mode interactions-mode}]
|
||||
|
||||
|
||||
[:& (mf/provider ctx/current-zoom) {:value zoom}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
|
||||
|
||||
(mf/defc header-options
|
||||
[{:keys [section zoom page file index permissions]}]
|
||||
[{:keys [section zoom page file index permissions interactions-mode]}]
|
||||
(let [fullscreen? (mf/deref fullscreen-ref)
|
||||
|
||||
toggle-fullscreen
|
||||
@@ -95,7 +95,7 @@
|
||||
:interactions [:*
|
||||
(when index
|
||||
[:& flows-menu {:page page :index index}])
|
||||
[:& interactions-menu]]
|
||||
[:& interactions-menu {:interactions-mode interactions-mode}]]
|
||||
:comments [:& comments-menu]
|
||||
|
||||
[:div.view-options])
|
||||
@@ -184,7 +184,7 @@
|
||||
|
||||
|
||||
(mf/defc header
|
||||
[{:keys [project file page frame zoom section permissions index]}]
|
||||
[{:keys [project file page frame zoom section permissions index interactions-mode]}]
|
||||
(let [go-to-dashboard
|
||||
#(st/emit! (dv/go-to-dashboard))
|
||||
|
||||
@@ -238,4 +238,5 @@
|
||||
:page page
|
||||
:file file
|
||||
:index index
|
||||
:zoom zoom}]]))
|
||||
:zoom zoom
|
||||
:interactions-mode interactions-mode}]]))
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.data.comments :as dcm]
|
||||
[app.main.data.viewer :as dv]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.hooks :as h]
|
||||
@@ -201,18 +200,19 @@
|
||||
[:span.label (:name flow)]])]]])))
|
||||
|
||||
(mf/defc interactions-menu
|
||||
[]
|
||||
(let [local (mf/deref refs/viewer-local)
|
||||
mode (:interactions-mode local)
|
||||
|
||||
show-dropdown? (mf/use-state false)
|
||||
[{:keys [interactions-mode]}]
|
||||
(let [show-dropdown? (mf/use-state false)
|
||||
toggle-dropdown (mf/use-fn #(swap! show-dropdown? not))
|
||||
hide-dropdown (mf/use-fn #(reset! show-dropdown? false))
|
||||
|
||||
select-mode
|
||||
(mf/use-callback
|
||||
(fn [mode]
|
||||
(st/emit! (dv/set-interactions-mode mode))))]
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(let [mode (some-> (dom/get-current-target event)
|
||||
(dom/get-data "mode")
|
||||
(keyword))]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dv/set-interactions-mode mode)))))]
|
||||
|
||||
[:div.view-options {:on-click toggle-dropdown}
|
||||
[:span.label (tr "viewer.header.interactions")]
|
||||
@@ -220,18 +220,21 @@
|
||||
[:& dropdown {:show @show-dropdown?
|
||||
:on-close hide-dropdown}
|
||||
[:ul.dropdown.with-check
|
||||
[:li {:class (dom/classnames :selected (= mode :hide))
|
||||
:on-click #(select-mode :hide)}
|
||||
[:li {:class (dom/classnames :selected (= interactions-mode :hide))
|
||||
:on-click select-mode
|
||||
:data-mode :hide}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "viewer.header.dont-show-interactions")]]
|
||||
|
||||
[:li {:class (dom/classnames :selected (= mode :show))
|
||||
:on-click #(select-mode :show)}
|
||||
[:li {:class (dom/classnames :selected (= interactions-mode :show))
|
||||
:on-click select-mode
|
||||
:data-mode :show}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "viewer.header.show-interactions")]]
|
||||
|
||||
[:li {:class (dom/classnames :selected (= mode :show-on-click))
|
||||
:on-click #(select-mode :show-on-click)}
|
||||
[:li {:class (dom/classnames :selected (= interactions-mode :show-on-click))
|
||||
:on-click select-mode
|
||||
:data-mode :show-on-click}
|
||||
[:span.icon i/tick]
|
||||
[:span.label (tr "viewer.header.show-interactions-on-click")]]]]]))
|
||||
|
||||
|
||||
@@ -77,6 +77,7 @@
|
||||
|
||||
on-thread-click
|
||||
(mf/use-callback
|
||||
(mf/deps page-id)
|
||||
(fn [thread]
|
||||
(when (not= page-id (:page-id thread))
|
||||
(st/emit! (dw/go-to-page (:page-id thread))))
|
||||
|
||||
@@ -388,7 +388,7 @@
|
||||
(mf/defc context-menu-prototype
|
||||
[{:keys [shapes]}]
|
||||
(let [options (mf/deref refs/workspace-page-options)
|
||||
options-mode (mf/deref refs/options-mode)
|
||||
options-mode (mf/deref refs/options-mode-global)
|
||||
do-add-flow #(st/emit! (dwi/add-flow-selected-frame))
|
||||
do-remove-flow #(st/emit! (dwi/remove-flow (:id %)))
|
||||
flows (:flows options)
|
||||
@@ -439,7 +439,8 @@
|
||||
|
||||
has-component? (some true? (map #(contains? % :component-id) shapes))
|
||||
is-component? (and single? (-> shapes first :component-id some?))
|
||||
is-non-root? (and single? (ctk/in-component-copy-not-root? (first shapes)))
|
||||
in-copy-not-root? (some true? (map #(ctk/in-component-copy-not-root? %) shapes))
|
||||
|
||||
objects (deref refs/workspace-page-objects)
|
||||
touched? (and single? (cph/component-touched? objects (:id (first shapes))))
|
||||
can-update-main? (or (not components-v2) touched?)
|
||||
@@ -460,8 +461,11 @@
|
||||
is-dangling? (nil? (if local-component?
|
||||
(ctkl/get-component workspace-data component-id)
|
||||
(ctf/get-component workspace-libraries component-file component-id)))
|
||||
lib-exists? (and (not local-component?)
|
||||
(some? (get workspace-libraries component-file)))
|
||||
|
||||
do-add-component #(st/emit! (dwl/add-component))
|
||||
do-add-multiple-components #(st/emit! (dwl/add-multiple-components))
|
||||
do-detach-component #(st/emit! (dwl/detach-component id))
|
||||
do-detach-component-in-bulk #(st/emit! dwl/detach-selected-components)
|
||||
do-reset-component #(st/emit! (dwl/reset-component id))
|
||||
@@ -504,12 +508,15 @@
|
||||
:on-accept do-update-component-in-bulk}))))]
|
||||
[:*
|
||||
[:*
|
||||
(when (or (not is-non-root?) (and has-component? (not single?)))
|
||||
(when (or (not in-copy-not-root?) (and has-component? (not single?)))
|
||||
[:& menu-separator])
|
||||
(when-not is-non-root?
|
||||
(when-not in-copy-not-root?
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.create-component")
|
||||
:shortcut (sc/get-tooltip :create-component)
|
||||
:on-click do-add-component}])
|
||||
(when-not (or single? in-copy-not-root?)
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.create-multiple-components")
|
||||
:on-click do-add-multiple-components}])
|
||||
(when (and has-component? (not single?))
|
||||
[:*
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.detach-instances-in-bulk")
|
||||
@@ -563,7 +570,7 @@
|
||||
(when can-update-main?
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.reset-overrides")
|
||||
:on-click do-reset-component}])
|
||||
(when components-v2
|
||||
(when (and components-v2 lib-exists?)
|
||||
[:& menu-entry {:title (tr "workspace.shape.menu.restore-main")
|
||||
:on-click do-restore-component}])]
|
||||
[:*
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
[app.main.snap :as snap]
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.workspace.shapes.path.common :as pc]
|
||||
[app.util.dom :as dom]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[app.main.data.workspace.texts :as dwt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.shapes.text.styles :as sts]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.keyboard :as kbd]
|
||||
|
||||
@@ -167,6 +167,9 @@
|
||||
(ctkl/get-component workspace-data component-id)
|
||||
(ctf/get-component workspace-libraries library-id component-id))
|
||||
is-dangling? (nil? component)
|
||||
lib-exists? (and (not local-component?)
|
||||
(some? (get workspace-libraries library-id)))
|
||||
|
||||
|
||||
on-menu-click
|
||||
(mf/use-callback
|
||||
@@ -242,22 +245,22 @@
|
||||
|
||||
[[(tr "workspace.shape.menu.detach-instance") do-detach-component]
|
||||
(when can-update-main?
|
||||
[:*
|
||||
[(tr "workspace.shape.menu.reset-overrides") do-reset-component]
|
||||
[(tr "workspace.shape.menu.update-main") do-update-component]])
|
||||
[(tr "workspace.shape.menu.reset-overrides") do-reset-component])
|
||||
(when can-update-main?
|
||||
[(tr "workspace.shape.menu.update-main") do-update-component])
|
||||
[(tr "workspace.shape.menu.show-main") do-show-component]])
|
||||
|
||||
(if is-dangling?
|
||||
[[(tr "workspace.shape.menu.detach-instance") do-detach-component]
|
||||
(when can-update-main?
|
||||
[(tr "workspace.shape.menu.reset-overrides") do-reset-component])
|
||||
(when components-v2
|
||||
(when (and components-v2 lib-exists?)
|
||||
[(tr "workspace.shape.menu.restore-main") do-restore-component])]
|
||||
[[(tr "workspace.shape.menu.detach-instance") do-detach-component]
|
||||
(when can-update-main?
|
||||
[:*
|
||||
[(tr "workspace.shape.menu.reset-overrides") do-reset-component]
|
||||
[(tr "workspace.shape.menu.update-main") do-update-remote-component]])
|
||||
[(tr "workspace.shape.menu.reset-overrides") do-reset-component])
|
||||
(when can-update-main?
|
||||
[(tr "workspace.shape.menu.update-main") do-update-remote-component])
|
||||
[(tr "workspace.shape.menu.go-main") do-navigate-component-file]])))}]]]
|
||||
|
||||
(when components-v2
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
type (unchecked-get props "type")
|
||||
values (unchecked-get props "values")
|
||||
|
||||
current-blend-mode (d/name (or (:blend-mode values) :normal))
|
||||
current-blend-mode (or (:blend-mode values) :normal)
|
||||
current-opacity (:opacity values)
|
||||
|
||||
state* (mf/use-state
|
||||
@@ -119,24 +119,24 @@
|
||||
options
|
||||
(mf/with-memo [current-blend-mode]
|
||||
(d/concat-vec
|
||||
(when (= "multiple" current-blend-mode)
|
||||
[{:value "multiple" :label "--"}])
|
||||
[{:value "normal" :label (tr "workspace.options.layer-options.blend-mode.normal")}
|
||||
{:value "darken" :label (tr "workspace.options.layer-options.blend-mode.darken")}
|
||||
{:value "multiply" :label (tr "workspace.options.layer-options.blend-mode.multiply")}
|
||||
{:value "color-burn" :label (tr "workspace.options.layer-options.blend-mode.color-burn")}
|
||||
{:value "lighten" :label (tr "workspace.options.layer-options.blend-mode.lighten")}
|
||||
{:value "screen" :label (tr "workspace.options.layer-options.blend-mode.screen")}
|
||||
{:value "color-dodge" :label (tr "workspace.options.layer-options.blend-mode.color-dodge")}
|
||||
{:value "overlay" :label (tr "workspace.options.layer-options.blend-mode.overlay")}
|
||||
{:value "soft-light" :label (tr "workspace.options.layer-options.blend-mode.soft-light")}
|
||||
{:value "hard-light" :label (tr "workspace.options.layer-options.blend-mode.hard-light")}
|
||||
{:value "difference" :label (tr "workspace.options.layer-options.blend-mode.difference")}
|
||||
{:value "exclusion" :label (tr "workspace.options.layer-options.blend-mode.exclusion")}
|
||||
{:value "hue" :label (tr "workspace.options.layer-options.blend-mode.hue")}
|
||||
{:value "saturation" :label (tr "workspace.options.layer-options.blend-mode.saturation")}
|
||||
{:value "color" :label (tr "workspace.options.layer-options.blend-mode.color")}
|
||||
{:value "luminosity" :label (tr "workspace.options.layer-options.blend-mode.luminosity")}]))]
|
||||
(when (= :multiple current-blend-mode)
|
||||
[{:value :multiple :label "--"}])
|
||||
[{:value :normal :label (tr "workspace.options.layer-options.blend-mode.normal")}
|
||||
{:value :darken :label (tr "workspace.options.layer-options.blend-mode.darken")}
|
||||
{:value :multiply :label (tr "workspace.options.layer-options.blend-mode.multiply")}
|
||||
{:value :color-burn :label (tr "workspace.options.layer-options.blend-mode.color-burn")}
|
||||
{:value :lighten :label (tr "workspace.options.layer-options.blend-mode.lighten")}
|
||||
{:value :screen :label (tr "workspace.options.layer-options.blend-mode.screen")}
|
||||
{:value :color-dodge :label (tr "workspace.options.layer-options.blend-mode.color-dodge")}
|
||||
{:value :overlay :label (tr "workspace.options.layer-options.blend-mode.overlay")}
|
||||
{:value :soft-light :label (tr "workspace.options.layer-options.blend-mode.soft-light")}
|
||||
{:value :hard-light :label (tr "workspace.options.layer-options.blend-mode.hard-light")}
|
||||
{:value :difference :label (tr "workspace.options.layer-options.blend-mode.difference")}
|
||||
{:value :exclusion :label (tr "workspace.options.layer-options.blend-mode.exclusion")}
|
||||
{:value :hue :label (tr "workspace.options.layer-options.blend-mode.hue")}
|
||||
{:value :saturation :label (tr "workspace.options.layer-options.blend-mode.saturation")}
|
||||
{:value :color :label (tr "workspace.options.layer-options.blend-mode.color")}
|
||||
{:value :luminosity :label (tr "workspace.options.layer-options.blend-mode.luminosity")}]))]
|
||||
|
||||
(mf/with-effect [current-blend-mode
|
||||
option-highlighted?
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
(:require-macros [app.main.style :refer [css]])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.refs :as refs]
|
||||
@@ -134,6 +135,7 @@
|
||||
(dom/classnames
|
||||
:element-list-body true
|
||||
:selected selected?))
|
||||
:data-test (dm/str "page-" id)
|
||||
:tab-index "0"
|
||||
:on-click navigate-fn
|
||||
:on-double-click on-double-click
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
:key (str "viewport" page-id)
|
||||
:view-box (utils/format-viewbox vbox)
|
||||
:ref on-viewport-ref
|
||||
:class (dm/str @cursor (when drawing-tool "drawing"))
|
||||
:class (dm/str @cursor (when drawing-tool " drawing"))
|
||||
:style {:touch-action "none"}
|
||||
:fill "none"
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.streams :as ms]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.formats :as fmt]
|
||||
[app.main.ui.workspace.viewport.rules :as rules]
|
||||
[app.util.dom :as dom]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
[app.main.data.workspace.undo :as dwu]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.workspace.shapes :as shapes]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.http :as http]
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.workspace.shapes.path.editor :refer [path-editor]]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.object :as obj]
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as mth]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.css-cursors :as cur]
|
||||
[app.main.ui.formats :refer [format-number]]))
|
||||
|
||||
(defn format-viewbox [vbox]
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
[app.common.types.components-list :as ctkl]
|
||||
[app.common.uri :as u]
|
||||
[app.main.data.fonts :as df]
|
||||
[app.main.features :as features]
|
||||
[app.main.features :as feat]
|
||||
[app.main.render :as render]
|
||||
[app.main.repo :as repo]
|
||||
[app.main.store :as st]
|
||||
@@ -52,6 +52,7 @@
|
||||
|
||||
(defn ^:export init
|
||||
[]
|
||||
(st/emit! (feat/initialize))
|
||||
(init-ui))
|
||||
|
||||
(defn reinit
|
||||
@@ -95,7 +96,7 @@
|
||||
|
||||
(mf/defc object-svg
|
||||
[{:keys [page-id file-id object-id render-embed?]}]
|
||||
(let [components-v2 (features/use-feature :components-v2)
|
||||
(let [components-v2 (feat/use-feature :components-v2)
|
||||
fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id object-id components-v2)
|
||||
(fn []
|
||||
@@ -135,7 +136,7 @@
|
||||
|
||||
(mf/defc objects-svg
|
||||
[{:keys [page-id file-id object-ids render-embed?]}]
|
||||
(let [components-v2 (features/use-feature :components-v2)
|
||||
(let [components-v2 (feat/use-feature :components-v2)
|
||||
fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id components-v2)
|
||||
(fn []
|
||||
|
||||
@@ -13,15 +13,25 @@
|
||||
(defn add-rule
|
||||
"Adds a CSS rule to a CSS Style Sheet"
|
||||
[styleSheet selector declarations]
|
||||
(.insertRule styleSheet (dm/str selector " {" (declarations->str declarations) "}")))
|
||||
(let [rule (dm/str selector " { " (declarations->str declarations) " }")]
|
||||
(.insertRule styleSheet rule (.-length (.-cssRules styleSheet)))))
|
||||
|
||||
(defn wrap-style-sheet
|
||||
[style]
|
||||
#js {:add (partial add-rule (.-sheet style))})
|
||||
|
||||
;; FIXME: Maybe we should rename this to `create-dynamic-style`?
|
||||
(defn create-style
|
||||
"Creates a new CSS Style Sheet and returns an object that allows adding rules to it"
|
||||
[]
|
||||
(let [style (dom/create-element "style")]
|
||||
(dom/set-attribute! style "type" "text/css")
|
||||
(dom/append-child! js/document.head style)
|
||||
(js-obj "add" (partial add-rule (.-sheet style)))))
|
||||
[id]
|
||||
(let [element (dom/get-element id)]
|
||||
(if (some? element)
|
||||
(wrap-style-sheet element)
|
||||
(let [style (dom/create-element "style")]
|
||||
(dom/set-attribute! style "id" id)
|
||||
(dom/set-attribute! style "type" "text/css")
|
||||
(dom/append-child! js/document.head style)
|
||||
(wrap-style-sheet style)))))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -154,7 +154,8 @@
|
||||
(let [page (current-page state)
|
||||
libraries (wsh/get-libraries state)
|
||||
|
||||
changes (pcb/empty-changes nil (:id page))
|
||||
changes (-> (pcb/empty-changes nil (:id page))
|
||||
(pcb/with-objects (:objects page)))
|
||||
|
||||
[new-shape changes]
|
||||
(dwlh/generate-instantiate-component changes
|
||||
|
||||
@@ -495,7 +495,7 @@ msgid "dashboard.export.title"
|
||||
msgstr "Export files"
|
||||
|
||||
msgid "dashboard.fonts.deleted-placeholder"
|
||||
msgstr "Font deleted"
|
||||
msgstr "Missing font"
|
||||
|
||||
#: src/app/main/ui/dashboard/fonts.cljs
|
||||
msgid "dashboard.fonts.dismiss-all"
|
||||
@@ -4367,6 +4367,9 @@ msgstr "Selection to board"
|
||||
msgid "workspace.shape.menu.create-component"
|
||||
msgstr "Create component"
|
||||
|
||||
msgid "workspace.shape.menu.create-multiple-components"
|
||||
msgstr "Create multiple components"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs
|
||||
msgid "workspace.shape.menu.cut"
|
||||
msgstr "Cut"
|
||||
|
||||
@@ -502,7 +502,7 @@ msgid "dashboard.export.title"
|
||||
msgstr "Exportar ficheros"
|
||||
|
||||
msgid "dashboard.fonts.deleted-placeholder"
|
||||
msgstr "Fuente eliminada"
|
||||
msgstr "Fuente no encontrada"
|
||||
|
||||
#: src/app/main/ui/dashboard/fonts.cljs
|
||||
msgid "dashboard.fonts.dismiss-all"
|
||||
@@ -4469,6 +4469,9 @@ msgstr "Tablero de selección"
|
||||
msgid "workspace.shape.menu.create-component"
|
||||
msgstr "Crear componente"
|
||||
|
||||
msgid "workspace.shape.menu.create-multiple-components"
|
||||
msgstr "Crear múltiples componentes"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs
|
||||
msgid "workspace.shape.menu.cut"
|
||||
msgstr "Cortar"
|
||||
|
||||
@@ -181,9 +181,9 @@ function build-docker-images {
|
||||
|
||||
pushd ./docker/images;
|
||||
|
||||
docker build -t penpotapp/frontend:$CURRENT_BRANCH -f Dockerfile.frontend .;
|
||||
docker build -t penpotapp/backend:$CURRENT_BRANCH -f Dockerfile.backend .;
|
||||
docker build -t penpotapp/exporter:$CURRENT_BRANCH -f Dockerfile.exporter .;
|
||||
docker build -t penpotapp/frontend:$CURRENT_BRANCH -t penpotapp/frontend:latest -f Dockerfile.frontend .;
|
||||
docker build -t penpotapp/backend:$CURRENT_BRANCH -t penpotapp/backend:latest -f Dockerfile.backend .;
|
||||
docker build -t penpotapp/exporter:$CURRENT_BRANCH -t penpotapp/exporter:latest -f Dockerfile.exporter .;
|
||||
|
||||
popd;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user