mirror of
https://github.com/penpot/penpot.git
synced 2025-07-28 05:59:23 +02:00
Merge remote-tracking branch 'origin/staging' into develop
This commit is contained in:
commit
bec59ab3c2
36 changed files with 449 additions and 359 deletions
|
@ -22,12 +22,21 @@
|
||||||
|
|
||||||
(defn enable-objects-map
|
(defn enable-objects-map
|
||||||
[file]
|
[file]
|
||||||
(let [update-fn #(d/update-when % :objects omap/wrap)]
|
(let [update-container
|
||||||
|
(fn [container]
|
||||||
|
(if (and (pmap/pointer-map? container)
|
||||||
|
(not (pmap/loaded? container)))
|
||||||
|
container
|
||||||
|
(d/update-when container :objects omap/wrap)))
|
||||||
|
|
||||||
|
update-data
|
||||||
|
(fn [fdata]
|
||||||
|
(-> fdata
|
||||||
|
(update :pages-index d/update-vals update-container)
|
||||||
|
(d/update-when :components d/update-vals update-container)))]
|
||||||
|
|
||||||
(-> file
|
(-> file
|
||||||
(update :data (fn [fdata]
|
(update :data update-data)
|
||||||
(-> fdata
|
|
||||||
(update :pages-index update-vals update-fn)
|
|
||||||
(d/update-when :components update-vals update-fn))))
|
|
||||||
(update :features conj "fdata/objects-map"))))
|
(update :features conj "fdata/objects-map"))))
|
||||||
|
|
||||||
(defn process-objects
|
(defn process-objects
|
||||||
|
@ -72,14 +81,15 @@
|
||||||
"Given a database connection and the final file-id, persist all
|
"Given a database connection and the final file-id, persist all
|
||||||
pointers to the underlying storage (the database)."
|
pointers to the underlying storage (the database)."
|
||||||
[system file-id]
|
[system file-id]
|
||||||
(doseq [[id item] @pmap/*tracked*]
|
(let [conn (db/get-connection system)]
|
||||||
(when (pmap/modified? item)
|
(doseq [[id item] @pmap/*tracked*]
|
||||||
(l/trc :hint "persist pointer" :file-id (str file-id) :id (str id))
|
(when (pmap/modified? item)
|
||||||
(let [content (-> item deref blob/encode)]
|
(l/trc :hint "persist pointer" :file-id (str file-id) :id (str id))
|
||||||
(db/insert! system :file-data-fragment
|
(let [content (-> item deref blob/encode)]
|
||||||
{:id id
|
(db/insert! conn :file-data-fragment
|
||||||
:file-id file-id
|
{:id id
|
||||||
:content content})))))
|
:file-id file-id
|
||||||
|
:content content}))))))
|
||||||
|
|
||||||
(defn process-pointers
|
(defn process-pointers
|
||||||
"Apply a function to all pointers on the file. Usuly used for
|
"Apply a function to all pointers on the file. Usuly used for
|
||||||
|
|
|
@ -30,7 +30,6 @@
|
||||||
[app.rpc.doc :as-alias doc]
|
[app.rpc.doc :as-alias doc]
|
||||||
[app.rpc.helpers :as rph]
|
[app.rpc.helpers :as rph]
|
||||||
[app.util.blob :as blob]
|
[app.util.blob :as blob]
|
||||||
[app.util.objects-map :as omap]
|
|
||||||
[app.util.pointer-map :as pmap]
|
[app.util.pointer-map :as pmap]
|
||||||
[app.util.services :as sv]
|
[app.util.services :as sv]
|
||||||
[app.util.time :as dt]
|
[app.util.time :as dt]
|
||||||
|
@ -119,18 +118,11 @@
|
||||||
[f]
|
[f]
|
||||||
(fn [cfg {:keys [id] :as file}]
|
(fn [cfg {:keys [id] :as file}]
|
||||||
(binding [pmap/*tracked* (pmap/create-tracked)
|
(binding [pmap/*tracked* (pmap/create-tracked)
|
||||||
pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)
|
pmap/*load-fn* (partial feat.fdata/load-pointer cfg id)]
|
||||||
cfeat/*wrap-with-pointer-map-fn* pmap/wrap]
|
|
||||||
(let [result (f cfg file)]
|
(let [result (f cfg file)]
|
||||||
(feat.fdata/persist-pointers! cfg id)
|
(feat.fdata/persist-pointers! cfg id)
|
||||||
result))))
|
result))))
|
||||||
|
|
||||||
(defn- wrap-with-objects-map-context
|
|
||||||
[f]
|
|
||||||
(fn [cfg file]
|
|
||||||
(binding [cfeat/*wrap-with-objects-map-fn* omap/wrap]
|
|
||||||
(f cfg file))))
|
|
||||||
|
|
||||||
(declare get-lagged-changes)
|
(declare get-lagged-changes)
|
||||||
(declare send-notifications!)
|
(declare send-notifications!)
|
||||||
(declare update-file)
|
(declare update-file)
|
||||||
|
@ -199,10 +191,7 @@
|
||||||
|
|
||||||
update-fn (cond-> update-file*
|
update-fn (cond-> update-file*
|
||||||
(contains? features "fdata/pointer-map")
|
(contains? features "fdata/pointer-map")
|
||||||
(wrap-with-pointer-map-context)
|
(wrap-with-pointer-map-context))
|
||||||
|
|
||||||
(contains? features "fdata/objects-map")
|
|
||||||
(wrap-with-objects-map-context))
|
|
||||||
|
|
||||||
changes (if changes-with-metadata
|
changes (if changes-with-metadata
|
||||||
(->> changes-with-metadata (mapcat :changes) vec)
|
(->> changes-with-metadata (mapcat :changes) vec)
|
||||||
|
@ -328,6 +317,7 @@
|
||||||
;; leeave it on lazy status
|
;; leeave it on lazy status
|
||||||
(-> (files/get-file cfg id :migrate? false)
|
(-> (files/get-file cfg id :migrate? false)
|
||||||
(update :data feat.fdata/process-pointers deref) ; ensure all pointers resolved
|
(update :data feat.fdata/process-pointers deref) ; ensure all pointers resolved
|
||||||
|
(update :data feat.fdata/process-objects (partial into {}))
|
||||||
(fmg/migrate-file))))))
|
(fmg/migrate-file))))))
|
||||||
(d/index-by :id)))
|
(d/index-by :id)))
|
||||||
|
|
||||||
|
|
|
@ -85,6 +85,14 @@
|
||||||
{:id id})
|
{:id id})
|
||||||
team))
|
team))
|
||||||
|
|
||||||
|
(defn get-raw-file
|
||||||
|
"Get the migrated data of one file."
|
||||||
|
([id] (get-raw-file (or *system* main/system) id))
|
||||||
|
([system id]
|
||||||
|
(db/run! system
|
||||||
|
(fn [system]
|
||||||
|
(files/get-file system id :migrate? false)))))
|
||||||
|
|
||||||
(defn reset-file-data!
|
(defn reset-file-data!
|
||||||
"Hardcode replace of the data of one file."
|
"Hardcode replace of the data of one file."
|
||||||
[system id data]
|
[system id data]
|
||||||
|
|
|
@ -68,6 +68,7 @@
|
||||||
(get-id [_])
|
(get-id [_])
|
||||||
(load! [_])
|
(load! [_])
|
||||||
(modified? [_])
|
(modified? [_])
|
||||||
|
(loaded? [_])
|
||||||
(clone [_]))
|
(clone [_]))
|
||||||
|
|
||||||
(deftype PointerMap [id mdata
|
(deftype PointerMap [id mdata
|
||||||
|
@ -90,6 +91,7 @@
|
||||||
(or odata {}))
|
(or odata {}))
|
||||||
|
|
||||||
(modified? [_] modified?)
|
(modified? [_] modified?)
|
||||||
|
(loaded? [_] loaded?)
|
||||||
(get-id [_] id)
|
(get-id [_] id)
|
||||||
|
|
||||||
(clone [this]
|
(clone [this]
|
||||||
|
@ -210,8 +212,6 @@
|
||||||
(defn create
|
(defn create
|
||||||
([]
|
([]
|
||||||
(let [id (uuid/next)
|
(let [id (uuid/next)
|
||||||
|
|
||||||
|
|
||||||
mdata (assoc *metadata* :created-at (dt/now))
|
mdata (assoc *metadata* :created-at (dt/now))
|
||||||
pmap (PointerMap. id mdata {} true true)]
|
pmap (PointerMap. id mdata {} true true)]
|
||||||
(some-> *tracked* (swap! assoc id pmap))
|
(some-> *tracked* (swap! assoc id pmap))
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
(ns app.common.data
|
(ns app.common.data
|
||||||
"A collection if helpers for working with data structures and other
|
"A collection if helpers for working with data structures and other
|
||||||
data resources."
|
data resources."
|
||||||
(:refer-clojure :exclude [read-string hash-map merge name
|
(:refer-clojure :exclude [read-string hash-map merge name update-vals
|
||||||
parse-double group-by iteration concat mapcat
|
parse-double group-by iteration concat mapcat
|
||||||
parse-uuid max min])
|
parse-uuid max min])
|
||||||
#?(:cljs
|
#?(:cljs
|
||||||
|
@ -403,6 +403,13 @@
|
||||||
[coll]
|
[coll]
|
||||||
(partial get coll))
|
(partial get coll))
|
||||||
|
|
||||||
|
(defn update-vals
|
||||||
|
[m f]
|
||||||
|
(reduce-kv (fn [acc k v]
|
||||||
|
(assoc acc k (f v)))
|
||||||
|
m
|
||||||
|
m))
|
||||||
|
|
||||||
(defn update-in-when
|
(defn update-in-when
|
||||||
[m key-seq f & args]
|
[m key-seq f & args]
|
||||||
(let [found (get-in m key-seq sentinel)]
|
(let [found (get-in m key-seq sentinel)]
|
||||||
|
|
|
@ -22,8 +22,8 @@
|
||||||
:proportion (/ width height)
|
:proportion (/ width height)
|
||||||
:proportion-lock true)))
|
:proportion-lock true)))
|
||||||
|
|
||||||
(defn setup-proportions-svg
|
(defn setup-proportions-size
|
||||||
[{:keys [width height] :as shape}]
|
[{{:keys [width height]} :selrect :as shape}]
|
||||||
(assoc shape
|
(assoc shape
|
||||||
:proportion (/ width height)
|
:proportion (/ width height)
|
||||||
:proportion-lock true))
|
:proportion-lock true))
|
||||||
|
@ -35,9 +35,11 @@
|
||||||
:proportion-lock false))
|
:proportion-lock false))
|
||||||
|
|
||||||
(defn setup-proportions
|
(defn setup-proportions
|
||||||
[shape]
|
[{:keys [type] :as shape}]
|
||||||
(case (:type shape)
|
(let [image-fill? (every? #(some? (:fill-image %)) (:fills shape))]
|
||||||
:svg-raw (setup-proportions-svg shape)
|
(cond
|
||||||
:image (setup-proportions-image shape)
|
(= type :svg-raw) (setup-proportions-size shape)
|
||||||
:text shape
|
(= type :image) (setup-proportions-image shape)
|
||||||
(setup-proportions-const shape)))
|
image-fill? (setup-proportions-size shape)
|
||||||
|
(= type :text) shape
|
||||||
|
:else (setup-proportions-const shape))))
|
||||||
|
|
|
@ -88,8 +88,8 @@
|
||||||
parent-selrect (:selrect parent)
|
parent-selrect (:selrect parent)
|
||||||
|
|
||||||
padding (when (and (not (nil? parent)) (> (count shapes) 0))
|
padding (when (and (not (nil? parent)) (> (count shapes) 0))
|
||||||
{:p1 (min (- min-y (:y1 parent-selrect)) (- (:y2 parent-selrect) max-y))
|
{:p1 (- min-y (:y1 parent-selrect))
|
||||||
:p2 (min (- min-x (:x1 parent-selrect)) (- (:x2 parent-selrect) max-x))})]
|
:p2 (- min-x (:x1 parent-selrect))})]
|
||||||
|
|
||||||
(cond-> {:layout-flex-dir direction :layout-gap layout-gap}
|
(cond-> {:layout-flex-dir direction :layout-gap layout-gap}
|
||||||
(not (nil? padding))
|
(not (nil? padding))
|
||||||
|
|
|
@ -52,7 +52,8 @@
|
||||||
[:width :int]
|
[:width :int]
|
||||||
[:height :int]
|
[:height :int]
|
||||||
[:mtype {:optional true} [:maybe :string]]
|
[:mtype {:optional true} [:maybe :string]]
|
||||||
[:id ::sm/uuid]])
|
[:id ::sm/uuid]
|
||||||
|
[:keep-aspect-ratio {:optional true} :boolean]])
|
||||||
|
|
||||||
(sm/define! ::gradient
|
(sm/define! ::gradient
|
||||||
[:map {:title "Gradient"}
|
[:map {:title "Gradient"}
|
||||||
|
|
|
@ -35,12 +35,6 @@
|
||||||
{:entries [app.main.ui.viewer]
|
{:entries [app.main.ui.viewer]
|
||||||
:depends-on #{:main :main-auth}}
|
:depends-on #{:main :main-auth}}
|
||||||
|
|
||||||
:main-onboarding
|
|
||||||
{:entries [app.main.ui.onboarding
|
|
||||||
app.main.ui.onboarding.questions
|
|
||||||
app.main.ui.releases]
|
|
||||||
:depends-on #{:main}}
|
|
||||||
|
|
||||||
:main-workspace
|
:main-workspace
|
||||||
{:entries [app.main.ui.workspace]
|
{:entries [app.main.ui.workspace]
|
||||||
:depends-on #{:main}}
|
:depends-on #{:main}}
|
||||||
|
|
|
@ -254,36 +254,39 @@
|
||||||
(prepare-restore-component nil library-data component-id it page (gpt/point 0 0) nil nil nil)))
|
(prepare-restore-component nil library-data component-id it page (gpt/point 0 0) nil nil nil)))
|
||||||
|
|
||||||
([changes library-data component-id it page delta old-id parent-id frame-id]
|
([changes library-data component-id it page delta old-id parent-id frame-id]
|
||||||
(let [component (ctkl/get-deleted-component library-data component-id)
|
(let [component (ctkl/get-deleted-component library-data component-id)
|
||||||
parent (get-in page [:objects parent-id])
|
parent (get-in page [:objects parent-id])
|
||||||
main-inst (get-in component [:objects (:main-instance-id component)])
|
main-inst (get-in component [:objects (:main-instance-id component)])
|
||||||
inside-component? (some? (ctn/get-instance-root (:objects page) parent))
|
inside-component? (some? (ctn/get-instance-root (:objects page) parent))
|
||||||
|
origin-frame (get-in page [:objects (:frame-id main-inst)])
|
||||||
|
;; We are using a deleted component andit's coordenates are absolute, we must adjust them to its containing frame to adjust the delta
|
||||||
|
delta (gpt/subtract delta (-> origin-frame :selrect gpt/point))
|
||||||
|
shapes (cfh/get-children-with-self (:objects component) (:main-instance-id component))
|
||||||
|
shapes (map #(gsh/move % delta) shapes)
|
||||||
|
|
||||||
shapes (cfh/get-children-with-self (:objects component) (:main-instance-id component))
|
first-shape (cond-> (first shapes)
|
||||||
shapes (map #(gsh/move % delta) shapes)
|
(not (nil? parent-id))
|
||||||
first-shape (cond-> (first shapes)
|
(assoc :parent-id parent-id)
|
||||||
(not (nil? parent-id))
|
(not (nil? frame-id))
|
||||||
(assoc :parent-id parent-id)
|
(assoc :frame-id frame-id)
|
||||||
(not (nil? frame-id))
|
(and (nil? frame-id) parent (= :frame (:type parent)))
|
||||||
(assoc :frame-id frame-id)
|
(assoc :frame-id parent-id)
|
||||||
(and (nil? frame-id) parent (= :frame (:type parent)))
|
(and (nil? frame-id) parent (not= :frame (:type parent)))
|
||||||
(assoc :frame-id parent-id)
|
(assoc :frame-id (:frame-id parent))
|
||||||
(and (nil? frame-id) parent (not= :frame (:type parent)))
|
inside-component?
|
||||||
(assoc :frame-id (:frame-id parent))
|
(dissoc :component-root)
|
||||||
inside-component?
|
(not inside-component?)
|
||||||
(dissoc :component-root)
|
(assoc :component-root true))
|
||||||
(not inside-component?)
|
|
||||||
(assoc :component-root true))
|
|
||||||
|
|
||||||
changes (-> (or changes (pcb/empty-changes it))
|
changes (-> (or changes (pcb/empty-changes it))
|
||||||
(pcb/with-page page)
|
(pcb/with-page page)
|
||||||
(pcb/with-objects (:objects page))
|
(pcb/with-objects (:objects page))
|
||||||
(pcb/with-library-data library-data))
|
(pcb/with-library-data library-data))
|
||||||
changes (cond-> (pcb/add-object changes first-shape {:ignore-touched true})
|
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
|
(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 (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
|
||||||
changes
|
changes
|
||||||
(rest shapes))]
|
(rest shapes))]
|
||||||
{:changes (pcb/restore-component changes component-id (:id page) main-inst)
|
{:changes (pcb/restore-component changes component-id (:id page) main-inst)
|
||||||
:shape (first shapes)})))
|
:shape (first shapes)})))
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,8 @@
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:mtype mtype
|
:mtype mtype
|
||||||
:id id}}]}]
|
:id id
|
||||||
|
:keep-aspect-ratio true}}]}]
|
||||||
(rx/of (dwsh/create-and-add-shape :rect x y shape))))))
|
(rx/of (dwsh/create-and-add-shape :rect x y shape))))))
|
||||||
|
|
||||||
(defn svg-uploaded
|
(defn svg-uploaded
|
||||||
|
@ -358,7 +359,8 @@
|
||||||
:id id
|
:id id
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:mtype mtype}}]
|
:mtype mtype
|
||||||
|
:keep-aspect-ratio true}}]
|
||||||
:name name
|
:name name
|
||||||
:frame-id (:id frame-shape)
|
:frame-id (:id frame-shape)
|
||||||
:parent-id (:id frame-shape)})]
|
:parent-id (:id frame-shape)})]
|
||||||
|
|
|
@ -90,8 +90,7 @@
|
||||||
|
|
||||||
(defmethod ptk/handle-error :default
|
(defmethod ptk/handle-error :default
|
||||||
[error]
|
[error]
|
||||||
(when-let [cause (::instance error)]
|
(st/async-emit! (rt/assign-exception error))
|
||||||
(ts/schedule #(st/emit! (rt/assign-exception cause))))
|
|
||||||
(print-group! "Unhandled Error"
|
(print-group! "Unhandled Error"
|
||||||
(fn []
|
(fn []
|
||||||
(print-trace! error)
|
(print-trace! error)
|
||||||
|
|
|
@ -15,6 +15,9 @@
|
||||||
[app.main.ui.frame-preview :as frame-preview]
|
[app.main.ui.frame-preview :as frame-preview]
|
||||||
[app.main.ui.icons :as i]
|
[app.main.ui.icons :as i]
|
||||||
[app.main.ui.messages :as msgs]
|
[app.main.ui.messages :as msgs]
|
||||||
|
[app.main.ui.onboarding :refer [onboarding-modal]]
|
||||||
|
[app.main.ui.onboarding.questions :refer [questions-modal]]
|
||||||
|
[app.main.ui.releases :refer [release-notes-modal]]
|
||||||
[app.main.ui.static :as static]
|
[app.main.ui.static :as static]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
|
@ -39,15 +42,6 @@
|
||||||
(def workspace-page
|
(def workspace-page
|
||||||
(mf/lazy-component app.main.ui.workspace/workspace))
|
(mf/lazy-component app.main.ui.workspace/workspace))
|
||||||
|
|
||||||
(def questions-modal
|
|
||||||
(mf/lazy-component app.main.ui.onboarding.questions/questions))
|
|
||||||
|
|
||||||
(def onboarding-modal
|
|
||||||
(mf/lazy-component app.main.ui.onboarding/onboarding-modal))
|
|
||||||
|
|
||||||
(def release-modal
|
|
||||||
(mf/lazy-component app.main.ui.releases/release-notes-modal))
|
|
||||||
|
|
||||||
(mf/defc on-main-error
|
(mf/defc on-main-error
|
||||||
[{:keys [error] :as props}]
|
[{:keys [error] :as props}]
|
||||||
(mf/with-effect
|
(mf/with-effect
|
||||||
|
@ -55,7 +49,8 @@
|
||||||
[:span "Internal application error"])
|
[:span "Internal application error"])
|
||||||
|
|
||||||
(mf/defc main-page
|
(mf/defc main-page
|
||||||
{::mf/wrap [#(mf/catch % {:fallback on-main-error})]}
|
{::mf/wrap [#(mf/catch % {:fallback on-main-error})]
|
||||||
|
::mf/props :obj}
|
||||||
[{:keys [route profile]}]
|
[{:keys [route profile]}]
|
||||||
(let [{:keys [data params]} route]
|
(let [{:keys [data params]} route]
|
||||||
[:& (mf/provider ctx/current-route) {:value route}
|
[:& (mf/provider ctx/current-route) {:value route}
|
||||||
|
@ -116,7 +111,7 @@
|
||||||
(:onboarding-viewed props)
|
(:onboarding-viewed props)
|
||||||
(not= (:release-notes-viewed props) (:main cf/version))
|
(not= (:release-notes-viewed props) (:main cf/version))
|
||||||
(not= "0.0" (:main cf/version)))
|
(not= "0.0" (:main cf/version)))
|
||||||
[:& release-modal {:version (:main cf/version)}]))
|
[:& release-notes-modal {:version (:main cf/version)}]))
|
||||||
|
|
||||||
(when profile
|
(when profile
|
||||||
[:& dashboard-page {:route route :profile profile}])]
|
[:& dashboard-page {:route route :profile profile}])]
|
||||||
|
|
|
@ -17,9 +17,9 @@
|
||||||
(mf/create-context nil))
|
(mf/create-context nil))
|
||||||
|
|
||||||
(mf/defc radio-button
|
(mf/defc radio-button
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj}
|
||||||
[props]
|
[props]
|
||||||
(let [context (mf/use-ctx context)
|
(let [context (mf/use-ctx context)
|
||||||
icon (unchecked-get props "icon")
|
icon (unchecked-get props "icon")
|
||||||
id (unchecked-get props "id")
|
id (unchecked-get props "id")
|
||||||
value (unchecked-get props "value")
|
value (unchecked-get props "value")
|
||||||
|
@ -27,7 +27,10 @@
|
||||||
title (unchecked-get props "title")
|
title (unchecked-get props "title")
|
||||||
unique-key (unchecked-get props "unique-key")
|
unique-key (unchecked-get props "unique-key")
|
||||||
icon-class (unchecked-get props "icon-class")
|
icon-class (unchecked-get props "icon-class")
|
||||||
type (or (unchecked-get props "type") "radio")
|
type (or (unchecked-get props "type")
|
||||||
|
(if (unchecked-get context "allow-empty")
|
||||||
|
"checkbox"
|
||||||
|
"radio"))
|
||||||
|
|
||||||
on-change (unchecked-get context "on-change")
|
on-change (unchecked-get context "on-change")
|
||||||
selected (unchecked-get context "selected")
|
selected (unchecked-get context "selected")
|
||||||
|
@ -59,14 +62,15 @@
|
||||||
:checked checked?}]]))
|
:checked checked?}]]))
|
||||||
|
|
||||||
(mf/defc radio-buttons
|
(mf/defc radio-buttons
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj}
|
||||||
[props]
|
[props]
|
||||||
(let [children (unchecked-get props "children")
|
(let [children (unchecked-get props "children")
|
||||||
on-change (unchecked-get props "on-change")
|
on-change (unchecked-get props "on-change")
|
||||||
selected (unchecked-get props "selected")
|
selected (unchecked-get props "selected")
|
||||||
name (unchecked-get props "name")
|
name (unchecked-get props "name")
|
||||||
class (unchecked-get props "class")
|
class (unchecked-get props "class")
|
||||||
wide (unchecked-get props "wide")
|
wide (unchecked-get props "wide")
|
||||||
|
allow-empty? (unchecked-get props "allow-empty")
|
||||||
|
|
||||||
encode-fn (d/nilv (unchecked-get props "encode-fn") identity)
|
encode-fn (d/nilv (unchecked-get props "encode-fn") identity)
|
||||||
decode-fn (d/nilv (unchecked-get props "encode-fn") identity)
|
decode-fn (d/nilv (unchecked-get props "encode-fn") identity)
|
||||||
|
@ -87,7 +91,8 @@
|
||||||
(mf/deps on-change)
|
(mf/deps on-change)
|
||||||
(fn [event]
|
(fn [event]
|
||||||
(let [input-node (dom/get-target event)
|
(let [input-node (dom/get-target event)
|
||||||
value (dom/get-target-val event)]
|
value (dom/get-target-val event)
|
||||||
|
value (when (not= value selected) value)]
|
||||||
(when (fn? on-change)
|
(when (fn? on-change)
|
||||||
(do (on-change (decode-fn value) event)
|
(do (on-change (decode-fn value) event)
|
||||||
(dom/blur! input-node))))))
|
(dom/blur! input-node))))))
|
||||||
|
@ -98,7 +103,8 @@
|
||||||
:on-change on-change'
|
:on-change on-change'
|
||||||
:name name
|
:name name
|
||||||
:encode-fn encode-fn
|
:encode-fn encode-fn
|
||||||
:decode-fn decode-fn})]
|
:decode-fn decode-fn
|
||||||
|
:allow-empty allow-empty?})]
|
||||||
|
|
||||||
[:& (mf/provider context) {:value context-value}
|
[:& (mf/provider context) {:value context-value}
|
||||||
[:div {:class (dm/str class " " (stl/css :radio-btn-wrapper))
|
[:div {:class (dm/str class " " (stl/css :radio-btn-wrapper))
|
||||||
|
|
|
@ -727,4 +727,5 @@
|
||||||
|
|
||||||
.email-input {
|
.email-input {
|
||||||
@extend .input-base;
|
@extend .input-base;
|
||||||
|
height: auto;
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,6 @@
|
||||||
:data-test "onboarding-next-btn"}
|
:data-test "onboarding-next-btn"}
|
||||||
(tr "labels.continue")]]]]))
|
(tr "labels.continue")]]]]))
|
||||||
|
|
||||||
|
|
||||||
(mf/defc onboarding-modal
|
(mf/defc onboarding-modal
|
||||||
{::mf/register modal/components
|
{::mf/register modal/components
|
||||||
::mf/register-as :onboarding}
|
::mf/register-as :onboarding}
|
||||||
|
|
|
@ -206,8 +206,11 @@
|
||||||
:default ""
|
:default ""
|
||||||
:name :team-size}]]]))
|
:name :team-size}]]]))
|
||||||
|
|
||||||
(mf/defc questions
|
;; NOTE: we don't register it on registry modal because we reference
|
||||||
[{:keys []}]
|
;; this modal directly on the ui namespace.
|
||||||
|
|
||||||
|
(mf/defc questions-modal
|
||||||
|
[]
|
||||||
(let [container (mf/use-ref)
|
(let [container (mf/use-ref)
|
||||||
step (mf/use-state 1)
|
step (mf/use-state 1)
|
||||||
clean-data (mf/use-state {})
|
clean-data (mf/use-state {})
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
;;; --- RELEASE NOTES MODAL
|
;;; --- RELEASE NOTES MODAL
|
||||||
|
|
||||||
(mf/defc release-notes
|
(mf/defc release-notes
|
||||||
{::mf/wrap-props false}
|
{::mf/props :obj}
|
||||||
[{:keys [version]}]
|
[{:keys [version]}]
|
||||||
(let [slide* (mf/use-state :start)
|
(let [slide* (mf/use-state :start)
|
||||||
slide (deref slide*)
|
slide (deref slide*)
|
||||||
|
@ -90,4 +90,4 @@
|
||||||
|
|
||||||
(defmethod rc/render-release-notes "0.0"
|
(defmethod rc/render-release-notes "0.0"
|
||||||
[params]
|
[params]
|
||||||
(rc/render-release-notes (assoc params :version "1.18")))
|
(rc/render-release-notes (assoc params :version "1.19")))
|
||||||
|
|
|
@ -20,52 +20,44 @@
|
||||||
[potok.v2.core :as ptk]
|
[potok.v2.core :as ptk]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(def ^:private go-settings-profile
|
||||||
|
#(st/emit! (rt/nav :settings-profile)))
|
||||||
|
|
||||||
|
(def ^:private go-settings-feedback
|
||||||
|
#(st/emit! (rt/nav :settings-feedback)))
|
||||||
|
|
||||||
|
(def ^:private go-settings-password
|
||||||
|
#(st/emit! (rt/nav :settings-password)))
|
||||||
|
|
||||||
|
(def ^:private go-settings-options
|
||||||
|
#(st/emit! (rt/nav :settings-options)))
|
||||||
|
|
||||||
|
(def ^:private go-settings-access-tokens
|
||||||
|
#(st/emit! (rt/nav :settings-access-tokens)))
|
||||||
|
|
||||||
|
(defn- show-release-notes
|
||||||
|
[event]
|
||||||
|
(let [version (:main cf/version)]
|
||||||
|
(st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version version}))
|
||||||
|
|
||||||
|
(if (and (kbd/alt? event) (kbd/mod? event))
|
||||||
|
(st/emit! (modal/show {:type :onboarding}))
|
||||||
|
(st/emit! (modal/show {:type :release-notes :version version})))))
|
||||||
|
|
||||||
(mf/defc sidebar-content
|
(mf/defc sidebar-content
|
||||||
[{:keys [profile section] :as props}]
|
{::mf/props :obj}
|
||||||
|
[{:keys [profile section]}]
|
||||||
(let [profile? (= section :settings-profile)
|
(let [profile? (= section :settings-profile)
|
||||||
password? (= section :settings-password)
|
password? (= section :settings-password)
|
||||||
options? (= section :settings-options)
|
options? (= section :settings-options)
|
||||||
feedback? (= section :settings-feedback)
|
feedback? (= section :settings-feedback)
|
||||||
access-tokens? (= section :settings-access-tokens)
|
access-tokens? (= section :settings-access-tokens)
|
||||||
|
team-id (du/get-current-team-id profile)
|
||||||
|
|
||||||
go-dashboard
|
go-dashboard
|
||||||
(mf/use-callback
|
(mf/use-fn
|
||||||
(mf/deps profile)
|
(mf/deps team-id)
|
||||||
#(st/emit! (rt/nav :dashboard-projects {:team-id (du/get-current-team-id profile)})))
|
#(st/emit! (rt/nav :dashboard-projects {:team-id team-id})))]
|
||||||
|
|
||||||
go-settings-profile
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps profile)
|
|
||||||
#(st/emit! (rt/nav :settings-profile)))
|
|
||||||
|
|
||||||
go-settings-feedback
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps profile)
|
|
||||||
#(st/emit! (rt/nav :settings-feedback)))
|
|
||||||
|
|
||||||
go-settings-password
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps profile)
|
|
||||||
#(st/emit! (rt/nav :settings-password)))
|
|
||||||
|
|
||||||
go-settings-options
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps profile)
|
|
||||||
#(st/emit! (rt/nav :settings-options)))
|
|
||||||
|
|
||||||
go-settings-access-tokens
|
|
||||||
(mf/use-callback
|
|
||||||
(mf/deps profile)
|
|
||||||
#(st/emit! (rt/nav :settings-access-tokens)))
|
|
||||||
|
|
||||||
show-release-notes
|
|
||||||
(mf/use-callback
|
|
||||||
(fn [event]
|
|
||||||
(let [version (:main cf/version)]
|
|
||||||
(st/emit! (ptk/event ::ev/event {::ev/name "show-release-notes" :version version}))
|
|
||||||
(if (and (kbd/alt? event) (kbd/mod? event))
|
|
||||||
(st/emit! (modal/show {:type :onboarding}))
|
|
||||||
(st/emit! (modal/show {:type :release-notes :version version}))))))]
|
|
||||||
|
|
||||||
[:div {:class (stl/css :sidebar-content)}
|
[:div {:class (stl/css :sidebar-content)}
|
||||||
[:div {:class (stl/css :sidebar-content-section)}
|
[:div {:class (stl/css :sidebar-content-section)}
|
||||||
|
@ -73,6 +65,7 @@
|
||||||
:on-click go-dashboard}
|
:on-click go-dashboard}
|
||||||
[:span {:class (stl/css :icon)} i/arrow-down]
|
[:span {:class (stl/css :icon)} i/arrow-down]
|
||||||
[:span {:class (stl/css :text)} (tr "labels.dashboard")]]]
|
[:span {:class (stl/css :text)} (tr "labels.dashboard")]]]
|
||||||
|
|
||||||
[:hr]
|
[:hr]
|
||||||
|
|
||||||
[:div {:class (stl/css :sidebar-content-section)}
|
[:div {:class (stl/css :sidebar-content-section)}
|
||||||
|
@ -108,7 +101,8 @@
|
||||||
[:span {:class (stl/css :element-title)} (tr "labels.give-feedback")]])]]]))
|
[:span {:class (stl/css :element-title)} (tr "labels.give-feedback")]])]]]))
|
||||||
|
|
||||||
(mf/defc sidebar
|
(mf/defc sidebar
|
||||||
{::mf/wrap [mf/memo]}
|
{::mf/wrap [mf/memo]
|
||||||
|
::mf/props :obj}
|
||||||
[{:keys [profile locale section]}]
|
[{:keys [profile locale section]}]
|
||||||
[:div {:class (stl/css :dashboard-sidebar :settings)}
|
[:div {:class (stl/css :dashboard-sidebar :settings)}
|
||||||
[:& sidebar-content {:profile profile
|
[:& sidebar-content {:profile profile
|
||||||
|
|
|
@ -117,9 +117,10 @@
|
||||||
:style style}]
|
:style style}]
|
||||||
(if (:fill-image value)
|
(if (:fill-image value)
|
||||||
(let [uri (cf/resolve-file-media (:fill-image value))
|
(let [uri (cf/resolve-file-media (:fill-image value))
|
||||||
|
keep-ar? (-> value :fill-image :keep-aspect-ratio)
|
||||||
image-props #js {:id (dm/str "fill-image-" render-id "-" fill-index)
|
image-props #js {:id (dm/str "fill-image-" render-id "-" fill-index)
|
||||||
:href (get uris uri uri)
|
:href (get uris uri uri)
|
||||||
:preserveAspectRatio "xMidYMid slice"
|
:preserveAspectRatio (if keep-ar? "xMidYMid slice" "none")
|
||||||
:width width
|
:width width
|
||||||
:height height
|
:height height
|
||||||
:key (dm/str fill-index)
|
:key (dm/str fill-index)
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
grid-template-areas: "left-sidebar viewport right-sidebar";
|
grid-template-areas: "left-sidebar viewport right-sidebar";
|
||||||
grid-template-rows: 1fr;
|
grid-template-rows: 1fr;
|
||||||
grid-template-columns: auto 1fr auto;
|
grid-template-columns: auto 1fr auto;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.workspace-loader {
|
.workspace-loader {
|
||||||
@include flexCenter;
|
@include flexCenter;
|
||||||
|
|
|
@ -84,7 +84,10 @@
|
||||||
on-fill-image-success
|
on-fill-image-success
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [image]
|
(fn [image]
|
||||||
(st/emit! (dc/update-colorpicker-color {:image (select-keys image [:id :width :height :mtype :name])} (not @drag?)))))
|
(st/emit! (dc/update-colorpicker-color
|
||||||
|
{:image (-> (select-keys image [:id :width :height :mtype :name])
|
||||||
|
(assoc :keep-aspect-ratio true))}
|
||||||
|
(not @drag?)))))
|
||||||
|
|
||||||
on-fill-image-click
|
on-fill-image-click
|
||||||
(mf/use-callback #(dom/click (mf/ref-val fill-image-ref)))
|
(mf/use-callback #(dom/click (mf/ref-val fill-image-ref)))
|
||||||
|
@ -94,6 +97,16 @@
|
||||||
(fn [file]
|
(fn [file]
|
||||||
(st/emit! (dwm/upload-fill-image file on-fill-image-success))))
|
(st/emit! (dwm/upload-fill-image file on-fill-image-success))))
|
||||||
|
|
||||||
|
handle-change-keep-aspect-ratio
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps current-color)
|
||||||
|
(fn []
|
||||||
|
(let [keep-aspect-ratio? (-> current-color :image :keep-aspect-ratio not)]
|
||||||
|
(st/emit! (dc/update-colorpicker-color
|
||||||
|
{:image (-> (:image current-color)
|
||||||
|
(assoc :keep-aspect-ratio keep-aspect-ratio?))}
|
||||||
|
true)))))
|
||||||
|
|
||||||
set-tab!
|
set-tab!
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn [event]
|
(fn [event]
|
||||||
|
@ -248,11 +261,24 @@
|
||||||
:on-select-stop handle-change-stop}])
|
:on-select-stop handle-change-stop}])
|
||||||
|
|
||||||
(if (= selected-mode :image)
|
(if (= selected-mode :image)
|
||||||
(let [uri (cfg/resolve-file-media (:image current-color))]
|
(let [uri (cfg/resolve-file-media (:image current-color))
|
||||||
|
keep-aspect-ratio? (-> current-color :image :keep-aspect-ratio)]
|
||||||
[:div {:class (stl/css :select-image)}
|
[:div {:class (stl/css :select-image)}
|
||||||
[:div {:class (stl/css :content)}
|
[:div {:class (stl/css :content)}
|
||||||
(when (:image current-color)
|
(when (:image current-color)
|
||||||
[:img {:src uri}])]
|
[:img {:src uri}])]
|
||||||
|
|
||||||
|
(when (some? (:image current-color))
|
||||||
|
[:div {:class (stl/css :checkbox-option)}
|
||||||
|
[:label {:for "keep-aspect-ratio"
|
||||||
|
:class (stl/css-case :global/checked keep-aspect-ratio?)}
|
||||||
|
[:span {:class (stl/css-case :global/checked keep-aspect-ratio?)}
|
||||||
|
(when keep-aspect-ratio? i/status-tick-refactor)]
|
||||||
|
(tr "media.keep-aspect-ratio")
|
||||||
|
[:input {:type "checkbox"
|
||||||
|
:id "keep-aspect-ratio"
|
||||||
|
:checked keep-aspect-ratio?
|
||||||
|
:on-change handle-change-keep-aspect-ratio}]]])
|
||||||
[:button
|
[:button
|
||||||
{:class (stl/css :choose-image)
|
{:class (stl/css :choose-image)
|
||||||
:title (tr "media.choose-image")
|
:title (tr "media.choose-image")
|
||||||
|
|
|
@ -166,3 +166,8 @@
|
||||||
margin-top: $s-12;
|
margin-top: $s-12;
|
||||||
height: $s-32;
|
height: $s-32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.checkbox-option {
|
||||||
|
@extend .input-checkbox;
|
||||||
|
margin: $s-16 0 0 0;
|
||||||
|
}
|
||||||
|
|
|
@ -39,10 +39,7 @@
|
||||||
&.wide {
|
&.wide {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
&.mid-palette,
|
|
||||||
&.small-palette {
|
|
||||||
grid-template-columns: $s-64 auto 1fr;
|
|
||||||
}
|
|
||||||
.resize-area {
|
.resize-area {
|
||||||
grid-area: resize;
|
grid-area: resize;
|
||||||
height: $s-8;
|
height: $s-8;
|
||||||
|
@ -108,56 +105,62 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.handler {
|
}
|
||||||
@include buttonStyle;
|
|
||||||
@include flexCenter;
|
.handler {
|
||||||
width: $s-12;
|
@include buttonStyle;
|
||||||
|
@include flexCenter;
|
||||||
|
width: $s-12;
|
||||||
|
height: 100%;
|
||||||
|
.handler-btn {
|
||||||
|
width: $s-4;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
.handler-btn {
|
max-height: $s-40;
|
||||||
width: $s-4;
|
margin: $s-8 $s-4;
|
||||||
height: 100%;
|
|
||||||
max-height: $s-40;
|
|
||||||
margin: $s-8 $s-4;
|
|
||||||
padding: 0;
|
|
||||||
border-radius: $s-4;
|
|
||||||
background-color: var(--palette-handler-background-color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.hidden-bts {
|
|
||||||
right: 10px;
|
|
||||||
z-index: 1;
|
|
||||||
width: 22px;
|
|
||||||
grid-template-columns: $s-8 auto 1fr;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
&.small-palette,
|
border-radius: $s-4;
|
||||||
&.mid-palette {
|
background-color: var(--palette-handler-background-color);
|
||||||
right: 10px;
|
}
|
||||||
}
|
}
|
||||||
.palette-btn-list {
|
|
||||||
opacity: $op-0;
|
.mid-palette,
|
||||||
visibility: hidden;
|
.small-palette {
|
||||||
width: 0;
|
grid-template-columns: $s-64 auto 1fr;
|
||||||
.palette-item {
|
}
|
||||||
opacity: $op-0;
|
|
||||||
visibility: hidden;
|
.hidden-bts {
|
||||||
z-index: 0;
|
right: $s-2;
|
||||||
}
|
z-index: $z-index-1;
|
||||||
}
|
width: 22px;
|
||||||
.resize-area {
|
grid-template-columns: $s-8 auto 1fr;
|
||||||
visibility: hidden;
|
padding: 0;
|
||||||
z-index: 0;
|
border-inline-start: 0;
|
||||||
width: 0;
|
border-start-start-radius: 0;
|
||||||
}
|
border-end-start-radius: 0;
|
||||||
.palette-actions {
|
.palette-btn-list {
|
||||||
visibility: hidden;
|
opacity: $op-0;
|
||||||
z-index: 0;
|
visibility: hidden;
|
||||||
}
|
width: 0;
|
||||||
.palette {
|
.palette-item {
|
||||||
visibility: hidden;
|
opacity: $op-0;
|
||||||
z-index: 0;
|
visibility: hidden;
|
||||||
}
|
z-index: 0;
|
||||||
.handler {
|
}
|
||||||
padding-bottom: $s-8;
|
}
|
||||||
}
|
.resize-area {
|
||||||
|
visibility: hidden;
|
||||||
|
z-index: 0;
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
.palette-actions {
|
||||||
|
visibility: hidden;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.palette {
|
||||||
|
visibility: hidden;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.handler {
|
||||||
|
padding-bottom: $s-8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -230,7 +230,8 @@
|
||||||
[{:keys [is-col? align-self on-change] :as props}]
|
[{:keys [is-col? align-self on-change] :as props}]
|
||||||
[:& radio-buttons {:selected (d/name align-self)
|
[:& radio-buttons {:selected (d/name align-self)
|
||||||
:on-change on-change
|
:on-change on-change
|
||||||
:name "flex-align-self"}
|
:name "flex-align-self"
|
||||||
|
:allow-empty true}
|
||||||
[:& radio-button {:value "start"
|
[:& radio-button {:value "start"
|
||||||
:icon (get-layout-flex-icon :align-self :start is-col?)
|
:icon (get-layout-flex-icon :align-self :start is-col?)
|
||||||
:title "Align self start"
|
:title "Align self start"
|
||||||
|
|
|
@ -27,6 +27,10 @@
|
||||||
[app.util.i18n :as i18n :refer [tr]]
|
[app.util.i18n :as i18n :refer [tr]]
|
||||||
[rumext.v2 :as mf]))
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
|
||||||
|
(def ^:private detach-icon
|
||||||
|
(i/icon-xref :detach-refactor (stl/css :detach-icon)))
|
||||||
|
|
||||||
(defn opacity->string
|
(defn opacity->string
|
||||||
[opacity]
|
[opacity]
|
||||||
(if (= opacity :multiple)
|
(if (= opacity :multiple)
|
||||||
|
@ -189,14 +193,14 @@
|
||||||
:dnd-over-top (= (:over dprops) :top)
|
:dnd-over-top (= (:over dprops) :top)
|
||||||
:dnd-over-bot (= (:over dprops) :bot))
|
:dnd-over-bot (= (:over dprops) :bot))
|
||||||
:ref dref}
|
:ref dref}
|
||||||
[:span {:class (stl/css :color-info)}
|
[:div {:class (stl/css :color-info)}
|
||||||
[:span {:class (stl/css-case :color-name-wrapper true
|
[:div {:class (stl/css-case :color-name-wrapper true
|
||||||
:no-opacity (or disable-opacity
|
:no-opacity (or disable-opacity
|
||||||
(not opacity?))
|
(not opacity?))
|
||||||
:library-name-wrapper library-color?
|
:library-name-wrapper library-color?
|
||||||
:editing editing-text?
|
:editing editing-text?
|
||||||
:gradient-name-wrapper gradient-color?)}
|
:gradient-name-wrapper gradient-color?)}
|
||||||
[:span {:class (stl/css :color-bullet-wrapper)}
|
[:div {:class (stl/css :color-bullet-wrapper)}
|
||||||
[:& cbn/color-bullet {:color (cond-> color
|
[:& cbn/color-bullet {:color (cond-> color
|
||||||
(nil? color-name) (assoc
|
(nil? color-name) (assoc
|
||||||
:id nil
|
:id nil
|
||||||
|
@ -218,7 +222,7 @@
|
||||||
:on-pointer-enter #(reset! hover-detach true)
|
:on-pointer-enter #(reset! hover-detach true)
|
||||||
:on-pointer-leave #(reset! hover-detach false)
|
:on-pointer-leave #(reset! hover-detach false)
|
||||||
:on-click detach-value}
|
:on-click detach-value}
|
||||||
i/detach-refactor])]
|
detach-icon])]
|
||||||
|
|
||||||
;; Rendering a gradient
|
;; Rendering a gradient
|
||||||
gradient-color?
|
gradient-color?
|
||||||
|
|
|
@ -10,14 +10,16 @@
|
||||||
@include flexRow;
|
@include flexRow;
|
||||||
|
|
||||||
&.dnd-over-top {
|
&.dnd-over-top {
|
||||||
border-top: $s-1 solid var(--layer-row-foreground-color-drag);
|
border-block-start: $s-1 solid var(--layer-row-foreground-color-drag);
|
||||||
}
|
}
|
||||||
&.dnd-over-bot {
|
&.dnd-over-bot {
|
||||||
border-bottom: $s-1 solid var(--layer-row-foreground-color-drag);
|
border-block-end: $s-1 solid var(--layer-row-foreground-color-drag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.color-info {
|
.color-info {
|
||||||
|
--detach-icon-foreground-color: none;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: $s-2;
|
gap: $s-2;
|
||||||
|
@ -26,147 +28,153 @@
|
||||||
height: $s-32;
|
height: $s-32;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
|
min-width: 0;
|
||||||
|
|
||||||
.color-name-wrapper {
|
&:hover {
|
||||||
@extend .input-element;
|
--detach-icon-foreground-color: var(--input-foreground-color-active);
|
||||||
flex-grow: 1;
|
|
||||||
width: 100%;
|
.detach-btn,
|
||||||
border-radius: $br-8 0 0 $br-8;
|
.select-btn {
|
||||||
padding: 0;
|
|
||||||
margin-right: 0;
|
|
||||||
gap: $s-4;
|
|
||||||
input {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
.color-bullet-wrapper {
|
|
||||||
height: $s-28;
|
|
||||||
padding: 0 $s-2 0 $s-8;
|
|
||||||
border-radius: $br-8 0 0 $br-8;
|
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
&:hover {
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.color-name {
|
}
|
||||||
@include titleTipography;
|
}
|
||||||
@include textEllipsis;
|
|
||||||
padding-inline: $s-6;
|
.color-name-wrapper {
|
||||||
|
@extend .input-element;
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 100%;
|
||||||
|
border-radius: $br-8 0 0 $br-8;
|
||||||
|
padding: 0;
|
||||||
|
margin-inline-end: 0;
|
||||||
|
gap: $s-4;
|
||||||
|
input {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.color-bullet-wrapper {
|
||||||
|
height: $s-28;
|
||||||
|
padding: 0 $s-2 0 $s-8;
|
||||||
|
border-radius: $br-8 0 0 $br-8;
|
||||||
|
background-color: transparent;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
&:hover {
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.color-name {
|
||||||
|
@include titleTipography;
|
||||||
|
@include textEllipsis;
|
||||||
|
padding-inline: $s-6;
|
||||||
|
border-radius: $br-8;
|
||||||
|
color: var(--input-foreground-color-active);
|
||||||
|
}
|
||||||
|
.detach-btn {
|
||||||
|
@extend .button-tertiary;
|
||||||
|
height: $s-28;
|
||||||
|
width: $s-28;
|
||||||
|
margin-inline-start: auto;
|
||||||
|
border-radius: 0 $br-8 $br-8 0;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.detach-icon {
|
||||||
|
@extend .button-icon;
|
||||||
|
stroke: var(--detach-icon-foreground-color);
|
||||||
|
}
|
||||||
|
.color-input-wrapper {
|
||||||
|
@include titleTipography;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: $s-28;
|
||||||
|
padding: 0 $s-0;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
background-color: var(--input-background-color);
|
||||||
|
color: var(--input-foreground-color);
|
||||||
|
border-radius: $br-0;
|
||||||
|
}
|
||||||
|
&.no-opacity {
|
||||||
|
border-radius: $br-8;
|
||||||
|
.color-input-wrapper {
|
||||||
border-radius: $br-8;
|
border-radius: $br-8;
|
||||||
color: var(--input-foreground-color-active);
|
}
|
||||||
|
}
|
||||||
|
&:hover {
|
||||||
|
--detach-icon-foreground-color: var(--input-foreground-color-active);
|
||||||
|
|
||||||
|
background-color: var(--input-background-color-hover);
|
||||||
|
border: $s-1 solid var(--input-border-color-hover);
|
||||||
|
.color-bullet-wrapper,
|
||||||
|
.color-name,
|
||||||
|
.detach-btn,
|
||||||
|
.color-input-wrapper {
|
||||||
|
background-color: var(--input-background-color-hover);
|
||||||
}
|
}
|
||||||
.detach-btn {
|
.detach-btn {
|
||||||
@extend .button-tertiary;
|
|
||||||
height: $s-28;
|
|
||||||
width: $s-28;
|
|
||||||
border-radius: 0 $br-8 $br-8 0;
|
|
||||||
display: none;
|
|
||||||
svg {
|
|
||||||
@extend .button-icon;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.color-input-wrapper {
|
|
||||||
@include titleTipography;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
|
||||||
height: $s-28;
|
|
||||||
padding: 0 $s-0;
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
flex-grow: 1;
|
|
||||||
background-color: var(--input-background-color);
|
|
||||||
color: var(--input-foreground-color);
|
|
||||||
border-radius: $br-0;
|
|
||||||
}
|
}
|
||||||
&.no-opacity {
|
&.editing {
|
||||||
border-radius: $br-8;
|
background-color: var(--input-background-color-active);
|
||||||
.color-input-wrapper {
|
|
||||||
border-radius: $br-8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:hover {
|
|
||||||
background-color: var(--input-background-color-hover);
|
|
||||||
border: $s-1 solid var(--input-border-color-hover);
|
|
||||||
.color-bullet-wrapper,
|
.color-bullet-wrapper,
|
||||||
.color-name,
|
.color-name,
|
||||||
.detach-btn,
|
.detach-btn,
|
||||||
.color-input-wrapper {
|
.color-input-wrapper {
|
||||||
background-color: var(--input-background-color-hover);
|
|
||||||
}
|
|
||||||
.detach-btn {
|
|
||||||
display: flex;
|
|
||||||
svg {
|
|
||||||
stroke: var(--input-foreground-color-active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&.editing {
|
|
||||||
background-color: var(--input-background-color-active);
|
background-color: var(--input-background-color-active);
|
||||||
.color-bullet-wrapper,
|
|
||||||
.color-name,
|
|
||||||
.detach-btn,
|
|
||||||
.color-input-wrapper {
|
|
||||||
background-color: var(--input-background-color-active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
&:focus,
|
|
||||||
&:focus-within {
|
|
||||||
background-color: var(--input-background-color-focus);
|
|
||||||
border: $s-1 solid var(--input-border-color-focus);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:focus,
|
&:focus,
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
background-color: var(--input-background-color-focus);
|
background-color: var(--input-background-color-focus);
|
||||||
border: $s-1 solid var(--input-border-color-focus);
|
border: $s-1 solid var(--input-border-color-focus);
|
||||||
&:hover {
|
|
||||||
background-color: var(--input-background-color-hover);
|
|
||||||
border: $s-1 solid var(--input-border-color-focus);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.editing {
|
|
||||||
background-color: var(--input-background-color-active);
|
|
||||||
&:hover {
|
|
||||||
border: $s-1 solid var(--input-border-color-active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.gradient-name-wrapper {
|
|
||||||
border-radius: 0 $br-8 $br-8 0;
|
|
||||||
.color-name {
|
|
||||||
@include flexRow;
|
|
||||||
border-radius: 0 $br-8 $br-8 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.library-name-wrapper {
|
|
||||||
border-radius: $br-8;
|
|
||||||
}
|
|
||||||
.opacity-element-wrapper {
|
|
||||||
@extend .input-element;
|
|
||||||
width: $s-60;
|
|
||||||
border-radius: 0 $br-8 $br-8 0;
|
|
||||||
.opacity-input {
|
|
||||||
padding: 0;
|
|
||||||
border-radius: 0 $br-8 $br-8 0;
|
|
||||||
min-width: $s-28;
|
|
||||||
}
|
|
||||||
.icon-text {
|
|
||||||
@include flexCenter;
|
|
||||||
height: $s-32;
|
|
||||||
margin-right: $s-4;
|
|
||||||
padding-top: $s-2;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&:hover {
|
&:focus,
|
||||||
.detach-btn,
|
&:focus-within {
|
||||||
.select-btn {
|
background-color: var(--input-background-color-focus);
|
||||||
background-color: transparent;
|
border: $s-1 solid var(--input-border-color-focus);
|
||||||
svg {
|
&:hover {
|
||||||
stroke: var(--input-foreground-color-active);
|
background-color: var(--input-background-color-hover);
|
||||||
}
|
border: $s-1 solid var(--input-border-color-focus);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.editing {
|
||||||
|
background-color: var(--input-background-color-active);
|
||||||
|
&:hover {
|
||||||
|
border: $s-1 solid var(--input-border-color-active);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient-name-wrapper {
|
||||||
|
border-radius: 0 $br-8 $br-8 0;
|
||||||
|
.color-name {
|
||||||
|
@include flexRow;
|
||||||
|
border-radius: 0 $br-8 $br-8 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.library-name-wrapper {
|
||||||
|
border-radius: $br-8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.opacity-element-wrapper {
|
||||||
|
@extend .input-element;
|
||||||
|
width: $s-60;
|
||||||
|
border-radius: 0 $br-8 $br-8 0;
|
||||||
|
.opacity-input {
|
||||||
|
padding: 0;
|
||||||
|
border-radius: 0 $br-8 $br-8 0;
|
||||||
|
min-width: $s-28;
|
||||||
|
}
|
||||||
|
.icon-text {
|
||||||
|
@include flexCenter;
|
||||||
|
height: $s-32;
|
||||||
|
margin-inline-end: $s-4;
|
||||||
|
margin-block-start: $s-2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.remove-btn,
|
.remove-btn,
|
||||||
|
|
|
@ -124,9 +124,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&.small-item {
|
&.small-item {
|
||||||
.typography-name {
|
|
||||||
height: $s-12;
|
|
||||||
}
|
|
||||||
.typography-data,
|
.typography-data,
|
||||||
.typography-font {
|
.typography-font {
|
||||||
display: none;
|
display: none;
|
||||||
|
|
|
@ -108,7 +108,7 @@
|
||||||
(when-not ^boolean read-only?
|
(when-not ^boolean read-only?
|
||||||
[:aside {:class (stl/css-case :main-toolbar true
|
[:aside {:class (stl/css-case :main-toolbar true
|
||||||
:not-rulers-present (not rulers?)
|
:not-rulers-present (not rulers?)
|
||||||
:hidden-toolbar hide-toolbar?)}
|
:main-toolbar-hidden hide-toolbar?)}
|
||||||
[:ul {:class (stl/css :main-toolbar-options)}
|
[:ul {:class (stl/css :main-toolbar-options)}
|
||||||
[:li
|
[:li
|
||||||
[:button
|
[:button
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
.main-toolbar {
|
.main-toolbar {
|
||||||
cursor: initial;
|
cursor: initial;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: $s-28;
|
|
||||||
left: calc(50% - $s-160);
|
left: calc(50% - $s-160);
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -24,6 +23,20 @@
|
||||||
top 0.3s,
|
top 0.3s,
|
||||||
height 0.3s,
|
height 0.3s,
|
||||||
opacity 0.3s;
|
opacity 0.3s;
|
||||||
|
&:not(.main-toolbar-hidden) {
|
||||||
|
top: $s-28;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-toolbar-hidden {
|
||||||
|
top: $s-24;
|
||||||
|
height: $s-16;
|
||||||
|
z-index: $z-index-1;
|
||||||
|
border-radius: 0 0 $s-8 $s-8;
|
||||||
|
border-block-start: 0;
|
||||||
|
.main-toolbar-options {
|
||||||
|
opacity: $op-0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-toolbar-options {
|
.main-toolbar-options {
|
||||||
|
@ -76,16 +89,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-toolbar.hidden-toolbar {
|
|
||||||
top: $s-20;
|
|
||||||
height: $s-16;
|
|
||||||
z-index: $z-index-1;
|
|
||||||
border-radius: 0 0 $s-8 $s-8;
|
|
||||||
.main-toolbar-options {
|
|
||||||
opacity: $op-0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-toolbar.not-rulers-present {
|
.main-toolbar.not-rulers-present {
|
||||||
top: $s-8;
|
top: $s-8;
|
||||||
&.hidden-toolbar {
|
&.hidden-toolbar {
|
||||||
|
|
|
@ -166,16 +166,16 @@
|
||||||
:y -11
|
:y -11
|
||||||
:width (max 0 (- text-width text-pos-x))
|
:width (max 0 (- text-width text-pos-x))
|
||||||
:height 20
|
:height 20
|
||||||
:class "workspace-frame-label"
|
:class (stl/css :workspace-frame-label-wrapper)
|
||||||
:style {:fill color}
|
:style {:fill color}
|
||||||
:visibility (if show-artboard-names? "visible" "hidden")
|
:visibility (if show-artboard-names? "visible" "hidden")}
|
||||||
:on-pointer-down on-pointer-down
|
|
||||||
:on-double-click on-double-click
|
|
||||||
:on-context-menu on-context-menu
|
|
||||||
:on-pointer-enter on-pointer-enter
|
|
||||||
:on-pointer-leave on-pointer-leave}
|
|
||||||
[:div {:class (stl/css :workspace-frame-label)
|
[:div {:class (stl/css :workspace-frame-label)
|
||||||
:style {:color color}}
|
:style {:color color}
|
||||||
|
:on-pointer-down on-pointer-down
|
||||||
|
:on-double-click on-double-click
|
||||||
|
:on-context-menu on-context-menu
|
||||||
|
:on-pointer-enter on-pointer-enter
|
||||||
|
:on-pointer-leave on-pointer-leave}
|
||||||
(if show-id?
|
(if show-id?
|
||||||
(dm/str (dm/str (:id frame)) " - " (:name frame))
|
(dm/str (dm/str (:id frame)) " - " (:name frame))
|
||||||
(:name frame))]]])))
|
(:name frame))]]])))
|
||||||
|
|
|
@ -63,10 +63,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.workspace-frame-label-wrapper {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
.workspace-frame-label {
|
.workspace-frame-label {
|
||||||
font-size: $fs-12;
|
font-size: $fs-12;
|
||||||
color: black;
|
color: black;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
max-width: fit-content;
|
||||||
|
pointer-events: all;
|
||||||
}
|
}
|
||||||
|
|
|
@ -347,7 +347,12 @@
|
||||||
(->> (upload-media-files context file-id name (:href image-data))
|
(->> (upload-media-files context file-id name (:href image-data))
|
||||||
(rx/catch #(do (.error js/console "Error uploading media: " name)
|
(rx/catch #(do (.error js/console "Error uploading media: " name)
|
||||||
(rx/of node)))
|
(rx/of node)))
|
||||||
(rx/map #(vector (:id image-data) %)))))
|
(rx/map (fn [data]
|
||||||
|
(let [data
|
||||||
|
(cond-> data
|
||||||
|
(some? (:keep-aspect-ratio image-data))
|
||||||
|
(assoc :keep-aspect-ratio (:keep-aspect-ratio image-data)))]
|
||||||
|
[(:id image-data) data]))))))
|
||||||
(rx/reduce (fn [acc [id data]] (assoc acc id data)) {})
|
(rx/reduce (fn [acc [id data]] (assoc acc id data)) {})
|
||||||
(rx/map
|
(rx/map
|
||||||
(fn [images]
|
(fn [images]
|
||||||
|
@ -360,7 +365,8 @@
|
||||||
(assoc-in [:attrs :penpot:media-width] (:width media))
|
(assoc-in [:attrs :penpot:media-width] (:width media))
|
||||||
(assoc-in [:attrs :penpot:media-height] (:height media))
|
(assoc-in [:attrs :penpot:media-height] (:height media))
|
||||||
(assoc-in [:attrs :penpot:media-mtype] (:mtype media))
|
(assoc-in [:attrs :penpot:media-mtype] (:mtype media))
|
||||||
|
(cond-> (some? (:keep-aspect-ratio media))
|
||||||
|
(assoc-in [:attrs :penpot:media-keep-aspect-ratio] (:keep-aspect-ratio media)))
|
||||||
(assoc-in [:attrs :penpot:fill-color] (:fill image-fill))
|
(assoc-in [:attrs :penpot:fill-color] (:fill image-fill))
|
||||||
(assoc-in [:attrs :penpot:fill-color-ref-file] (:fill-color-ref-file image-fill))
|
(assoc-in [:attrs :penpot:fill-color-ref-file] (:fill-color-ref-file image-fill))
|
||||||
(assoc-in [:attrs :penpot:fill-color-ref-id] (:fill-color-ref-id image-fill))
|
(assoc-in [:attrs :penpot:fill-color-ref-id] (:fill-color-ref-id image-fill))
|
||||||
|
|
|
@ -195,15 +195,22 @@
|
||||||
(d/deep-mapm
|
(d/deep-mapm
|
||||||
(fn [pair] (->> pair (mapv convert)))))))
|
(fn [pair] (->> pair (mapv convert)))))))
|
||||||
|
|
||||||
(def search-data-node? #{:rect :image :path :circle})
|
(def search-data-node? #{:rect :path :circle})
|
||||||
|
|
||||||
(defn get-svg-data
|
(defn get-svg-data
|
||||||
[type node]
|
[type node]
|
||||||
|
|
||||||
(let [node-attrs (add-attrs {} (:attrs node))]
|
(let [node-attrs (add-attrs {} (:attrs node))]
|
||||||
(cond
|
(cond
|
||||||
(search-data-node? type)
|
(search-data-node? type)
|
||||||
(let [data-tags #{:ellipse :rect :path :text :foreignObject :image}]
|
(let [data-tags #{:ellipse :rect :path :text :foreignObject}]
|
||||||
|
(->> node
|
||||||
|
(node-seq)
|
||||||
|
(filter #(contains? data-tags (:tag %)))
|
||||||
|
(map #(:attrs %))
|
||||||
|
(reduce add-attrs node-attrs)))
|
||||||
|
|
||||||
|
(= type :image)
|
||||||
|
(let [data-tags #{:rect :image}]
|
||||||
(->> node
|
(->> node
|
||||||
(node-seq)
|
(node-seq)
|
||||||
(filter #(contains? data-tags (:tag %)))
|
(filter #(contains? data-tags (:tag %)))
|
||||||
|
@ -523,7 +530,8 @@
|
||||||
(let [metadata {:id (get-meta node :media-id)
|
(let [metadata {:id (get-meta node :media-id)
|
||||||
:width (get-meta node :media-width)
|
:width (get-meta node :media-width)
|
||||||
:height (get-meta node :media-height)
|
:height (get-meta node :media-height)
|
||||||
:mtype (get-meta node :media-mtype)}]
|
:mtype (get-meta node :media-mtype)
|
||||||
|
:keep-aspect-ratio (get-meta node :media-keep-aspect-ratio str->bool)}]
|
||||||
(cond-> props
|
(cond-> props
|
||||||
(= type :image)
|
(= type :image)
|
||||||
(assoc :metadata metadata)
|
(assoc :metadata metadata)
|
||||||
|
@ -881,7 +889,8 @@
|
||||||
(let [id (get-in fill-node [:attrs :penpot:fill-image-id])
|
(let [id (get-in fill-node [:attrs :penpot:fill-image-id])
|
||||||
image-node (->> node (node-seq) (find-node-by-id id))]
|
image-node (->> node (node-seq) (find-node-by-id id))]
|
||||||
{:id id
|
{:id id
|
||||||
:href (get-in image-node [:attrs :href])})))
|
:href (get-in image-node [:attrs :href])
|
||||||
|
:keep-aspect-ratio (not= (get-in image-node [:attrs :preserveAspectRatio]) "none")})))
|
||||||
(filterv #(some? (:id %))))))
|
(filterv #(some? (:id %))))))
|
||||||
|
|
||||||
(defn has-fill-images?
|
(defn has-fill-images?
|
||||||
|
|
|
@ -5074,6 +5074,9 @@ msgstr "Gradient"
|
||||||
msgid "media.choose-image"
|
msgid "media.choose-image"
|
||||||
msgstr "Choose image"
|
msgstr "Choose image"
|
||||||
|
|
||||||
|
msgid "media.keep-aspect-ratio"
|
||||||
|
msgstr "Keep aspect ratio"
|
||||||
|
|
||||||
msgid "workspace.options.guides.title"
|
msgid "workspace.options.guides.title"
|
||||||
msgstr "Guides"
|
msgstr "Guides"
|
||||||
|
|
||||||
|
|
|
@ -5158,6 +5158,9 @@ msgstr "Gradiente"
|
||||||
msgid "media.choose-image"
|
msgid "media.choose-image"
|
||||||
msgstr "Elegir imagen"
|
msgstr "Elegir imagen"
|
||||||
|
|
||||||
|
msgid "media.keep-aspect-ratio"
|
||||||
|
msgstr "Mantener la proporción"
|
||||||
|
|
||||||
msgid "workspace.options.guides.title"
|
msgid "workspace.options.guides.title"
|
||||||
msgstr "Guías"
|
msgstr "Guías"
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue