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

This commit is contained in:
Andrey Antukh 2024-02-20 16:07:51 +01:00
commit 66f8ffb408
20 changed files with 446 additions and 330 deletions

View file

@ -36,6 +36,9 @@ export PENPOT_FLAGS="\
# Setup default upload media file size to 100MiB # Setup default upload media file size to 100MiB
export PENPOT_MEDIA_MAX_FILE_SIZE=104857600 export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
# export PENPOT_DATABASE_URI="postgresql://172.17.0.1:5432/penpot" # export PENPOT_DATABASE_URI="postgresql://172.17.0.1:5432/penpot"
# export PENPOT_DATABASE_USERNAME="penpot" # export PENPOT_DATABASE_USERNAME="penpot"
# export PENPOT_DATABASE_PASSWORD="penpot" # export PENPOT_DATABASE_PASSWORD="penpot"

View file

@ -37,6 +37,9 @@ export OPTIONS="
# Setup default upload media file size to 100MiB # Setup default upload media file size to 100MiB
export PENPOT_MEDIA_MAX_FILE_SIZE=104857600 export PENPOT_MEDIA_MAX_FILE_SIZE=104857600
# Setup default multipart upload size to 300MiB
export PENPOT_HTTP_SERVER_MAX_MULTIPART_BODY_SIZE=314572800
# Setup HEAP # Setup HEAP
# export OPTIONS="$OPTIONS -J-Xms50m -J-Xmx1024m" # export OPTIONS="$OPTIONS -J-Xms50m -J-Xmx1024m"
# export OPTIONS="$OPTIONS -J-Xms1100m -J-Xmx1100m -J-XX:+AlwaysPreTouch" # export OPTIONS="$OPTIONS -J-Xms1100m -J-Xmx1100m -J-XX:+AlwaysPreTouch"

View file

@ -65,7 +65,11 @@
(def ^:const buffer-size (:xnio/buffer-size yt/defaults)) (def ^:const buffer-size (:xnio/buffer-size yt/defaults))
(def ^:const penpot-magic-number 800099563638710213) (def ^:const penpot-magic-number 800099563638710213)
(def ^:const max-object-size (* 1024 1024 100)) ; Only allow 100MiB max file size.
;; A maximum (storage) object size allowed: 100MiB
(def ^:const max-object-size
(* 1024 1024 100))
(def ^:dynamic *position* nil) (def ^:dynamic *position* nil)

View file

@ -52,8 +52,8 @@
[_ cfg] [_ cfg]
(merge {::port 6060 (merge {::port 6060
::host "0.0.0.0" ::host "0.0.0.0"
::max-body-size (* 1024 1024 30) ; 30 MiB ::max-body-size (* 1024 1024 30) ; default 30 MiB
::max-multipart-body-size (* 1024 1024 120)} ; 120 MiB ::max-multipart-body-size (* 1024 1024 120)} ; default 120 MiB
(d/without-nils cfg))) (d/without-nils cfg)))
(defmethod ig/pre-init-spec ::server [_] (defmethod ig/pre-init-spec ::server [_]

View file

@ -347,6 +347,7 @@
(sv/defmethod ::get-file-fragment (sv/defmethod ::get-file-fragment
"Retrieve a file fragment by its ID. Only authenticated users." "Retrieve a file fragment by its ID. Only authenticated users."
{::doc/added "1.17" {::doc/added "1.17"
::rpc/auth false
::sm/params schema:get-file-fragment ::sm/params schema:get-file-fragment
::sm/result schema:file-fragment} ::sm/result schema:file-fragment}
[{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id fragment-id share-id]}] [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id fragment-id share-id]}]

View file

@ -40,6 +40,6 @@
(cond (cond
(= type :svg-raw) (setup-proportions-size shape) (= type :svg-raw) (setup-proportions-size shape)
(= type :image) (setup-proportions-image shape) (= type :image) (setup-proportions-image shape)
image-fill? (setup-proportions-size shape)
(= type :text) shape (= type :text) shape
image-fill? (setup-proportions-size shape)
:else (setup-proportions-const shape)))) :else (setup-proportions-const shape))))

View file

@ -15,8 +15,8 @@
(defn shape-stroke-margin (defn shape-stroke-margin
[shape stroke-width] [shape stroke-width]
(if (cfh/path-shape? shape) (if (cfh/path-shape? shape)
;; TODO: Calculate with the stroke offset (not implemented yet ;; TODO: Calculate with the stroke offset (not implemented yet)
(mth/sqrt (* 2 stroke-width stroke-width)) (+ stroke-width (mth/sqrt (* 2 stroke-width stroke-width)))
(- (mth/sqrt (* 2 stroke-width stroke-width)) stroke-width))) (- (mth/sqrt (* 2 stroke-width stroke-width)) stroke-width)))
(defn- apply-filters (defn- apply-filters
@ -104,7 +104,7 @@
0)) 0))
(reduce d/max 0)) (reduce d/max 0))
margin stroke-margin
(if ignore-margin? (if ignore-margin?
0 0
(shape-stroke-margin shape stroke-width)) (shape-stroke-margin shape stroke-width))
@ -124,9 +124,8 @@
:drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10) :drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10)
0)) 0))
(reduce d/max 0))] (reduce d/max 0))]
{:horizontal (mth/ceil (+ stroke-margin shadow-width))
{:horizontal (mth/ceil (+ stroke-width margin shadow-width)) :vertical (mth/ceil (+ stroke-margin shadow-height))})))
:vertical (mth/ceil (+ stroke-width margin shadow-height))})))
(defn- add-padding (defn- add-padding
[bounds padding] [bounds padding]
@ -143,13 +142,17 @@
(update :height + (* 2 v-padding))))) (update :height + (* 2 v-padding)))))
(defn calculate-base-bounds (defn calculate-base-bounds
[shape] ([shape]
(calculate-base-bounds shape true))
([shape ignore-margin?]
(-> (get-shape-filter-bounds shape) (-> (get-shape-filter-bounds shape)
(add-padding (calculate-padding shape true)))) (add-padding (calculate-padding shape ignore-margin?)))))
(defn get-object-bounds (defn get-object-bounds
[objects shape] ([objects shape]
(let [base-bounds (calculate-base-bounds shape) (get-object-bounds objects shape nil))
([objects shape {:keys [ignore-margin?] :or {ignore-margin? true}}]
(let [base-bounds (calculate-base-bounds shape ignore-margin?)
bounds bounds
(cond (cond
(or (empty? (:shapes shape)) (or (empty? (:shapes shape))
@ -185,5 +188,5 @@
filters (shape->filters shape) filters (shape->filters shape)
blur-value (or (-> shape :blur :value) 0)] blur-value (or (-> shape :blur :value) 0)]
(get-rect-filter-bounds children-bounds filters blur-value))) (get-rect-filter-bounds children-bounds filters blur-value))))

View file

@ -3,10 +3,10 @@ LABEL maintainer="Andrey Antukh <niwi@niwi.nz>"
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
ENV NODE_VERSION=v20.10.0 \ ENV NODE_VERSION=v20.11.1 \
CLOJURE_VERSION=1.11.1.1429 \ CLOJURE_VERSION=1.11.1.1435 \
CLJKONDO_VERSION=2023.12.15 \ CLJKONDO_VERSION=2024.02.12 \
BABASHKA_VERSION=1.3.187 \ BABASHKA_VERSION=1.3.188 \
CLJFMT_VERSION=0.12.0 \ CLJFMT_VERSION=0.12.0 \
LANG=en_US.UTF-8 \ LANG=en_US.UTF-8 \
LC_ALL=en_US.UTF-8 LC_ALL=en_US.UTF-8
@ -105,12 +105,12 @@ RUN set -eux; \
ARCH="$(dpkg --print-architecture)"; \ ARCH="$(dpkg --print-architecture)"; \
case "${ARCH}" in \ case "${ARCH}" in \
aarch64|arm64) \ aarch64|arm64) \
ESUM='aa43295803595f78d73e9c7c02866301c9729377277144e2829f54a58e5f6d21'; \ ESUM='3ce6a2b357e2ef45fd6b53d6587aa05bfec7771e7fb982f2c964f6b771b7526a'; \
BINARY_URL='https://corretto.aws/downloads/resources/21.0.1.12.1/amazon-corretto-21.0.1.12.1-linux-aarch64.tar.gz'; \ BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.2_13.tar.gz'; \
;; \ ;; \
amd64|x86_64) \ amd64|x86_64) \
ESUM='3e718a86cfa6c1173c469f5e9d6b07fa37381a28ebb1f80593250cc380baf22f'; \ ESUM='454bebb2c9fe48d981341461ffb6bf1017c7b7c6e15c6b0c29b959194ba3aaa5'; \
BINARY_URL='https://corretto.aws/downloads/resources/21.0.1.12.1/amazon-corretto-21.0.1.12.1-linux-x64.tar.gz'; \ BINARY_URL='https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/OpenJDK21U-jdk_x64_linux_hotspot_21.0.2_13.tar.gz'; \
;; \ ;; \
*) \ *) \
echo "Unsupported arch: ${ARCH}"; \ echo "Unsupported arch: ${ARCH}"; \

View file

@ -49,7 +49,7 @@ http {
listen 3449 default_server; listen 3449 default_server;
server_name _; server_name _;
client_max_body_size 100M; client_max_body_size 300M;
charset utf-8; charset utf-8;
proxy_http_version 1.1; proxy_http_version 1.1;

View file

@ -19,8 +19,8 @@
:git/url "https://github.com/funcool/beicon.git"} :git/url "https://github.com/funcool/beicon.git"}
funcool/rumext funcool/rumext
{:git/tag "v2.9.4" {:git/tag "v2.10"
:git/sha "af08e55" :git/sha "d96ea18"
:git/url "https://github.com/funcool/rumext.git"} :git/url "https://github.com/funcool/rumext.git"}
instaparse/instaparse {:mvn/version "1.4.12"} instaparse/instaparse {:mvn/version "1.4.12"}

View file

@ -273,6 +273,7 @@
--colorpicker-details-color: var(--color-background-quaternary); --colorpicker-details-color: var(--color-background-quaternary);
--colorpicker-details-color-selected: var(--color-accent-primary); --colorpicker-details-color-selected: var(--color-accent-primary);
--colorpicker-handlers-color: var(--color-foreground-primary); --colorpicker-handlers-color: var(--color-foreground-primary);
--colorpicker-background-color: var(--color-foreground-primary);
// COMMENTS // COMMENTS
--comment-title-color: var(--color-foreground-primary); --comment-title-color: var(--color-foreground-primary);
@ -400,4 +401,6 @@
--text-editor-selection-background-color: var(--la-tertiary-70); --text-editor-selection-background-color: var(--la-tertiary-70);
--expand-button-icon-border-width-selected: 2px; --expand-button-icon-border-width-selected: 2px;
--colorpicker-background-color: var(--color-background-primary);
} }

View file

@ -74,9 +74,11 @@
(assoc-in [:viewer-local :interactions-show?] interactions-show?))) (assoc-in [:viewer-local :interactions-show?] interactions-show?)))
ptk/WatchEvent ptk/WatchEvent
(watch [_ _ _] (watch [_ state _]
(rx/of (fetch-bundle (d/without-nils params)) (rx/of (fetch-bundle (d/without-nils params))
(fetch-comment-threads params))) ;; Only fetch threads for logged-in users
(when (some? (:profile state))
(fetch-comment-threads params))))
ptk/EffectEvent ptk/EffectEvent
(effect [_ _ _] (effect [_ _ _]

View file

@ -1618,7 +1618,7 @@
file-id (:current-file-id state) file-id (:current-file-id state)
frame-id (cfh/common-parent-frame objects selected) frame-id (cfh/common-parent-frame objects selected)
version (dm/get-in state [:workspace-data :version]) version (dm/get-in state [:workspace-file :version])
initial {:type :copied-shapes initial {:type :copied-shapes
:features features :features features

View file

@ -208,7 +208,10 @@
from-frame-id (if (cfh/frame-shape? from-shape) from-frame-id (if (cfh/frame-shape? from-shape)
from-id (:frame-id from-shape)) from-id (:frame-id from-shape))
target-frame (ctst/get-frame-by-position objects position)] target-frame
(->> (ctst/get-frames-by-position objects position)
(remove :hide-in-viewer)
(last))]
(when (and (not= (:id target-frame) uuid/zero) (when (and (not= (:id target-frame) uuid/zero)
(not= (:id target-frame) from-frame-id)) (not= (:id target-frame) from-frame-id))

View file

@ -449,7 +449,7 @@
(assoc :fills [])) (assoc :fills []))
{:keys [width height] :as bounds} (gsb/get-object-bounds objects object) {:keys [width height] :as bounds} (gsb/get-object-bounds objects object {:ignore-margin? false})
vbox (format-viewbox bounds) vbox (format-viewbox bounds)
fonts (ff/shape->fonts object objects) fonts (ff/shape->fonts object objects)

View file

@ -47,7 +47,7 @@
:center (/ (:stroke-width stroke 0) 2) :center (/ (:stroke-width stroke 0) 2)
:outer (:stroke-width stroke 0) :outer (:stroke-width stroke 0)
0) 0)
margin (gsb/shape-stroke-margin stroke stroke-width) stroke-margin (gsb/shape-stroke-margin shape stroke-width)
;; NOTE: for performance reasons we may can delimit a bit the ;; NOTE: for performance reasons we may can delimit a bit the
;; dependencies to really useful shape attrs instead of using ;; dependencies to really useful shape attrs instead of using
@ -57,8 +57,6 @@
(gst/shape->rect shape) (gst/shape->rect shape)
(grc/points->rect (:points shape)))) (grc/points->rect (:points shape))))
stroke-margin (+ stroke-width margin)
x (- (dm/get-prop selrect :x) stroke-margin) x (- (dm/get-prop selrect :x) stroke-margin)
y (- (dm/get-prop selrect :y) stroke-margin) y (- (dm/get-prop selrect :y) stroke-margin)
w (+ (dm/get-prop selrect :width) (* 2 stroke-margin)) w (+ (dm/get-prop selrect :width) (* 2 stroke-margin))

View file

@ -28,7 +28,8 @@
} }
.shape-row { .shape-row {
display: flex; display: grid;
grid-template-columns: auto 1fr;
gap: $s-8; gap: $s-8;
align-items: center; align-items: center;
height: $s-32; height: $s-32;
@ -46,6 +47,7 @@
.layer-title { .layer-title {
@include titleTipography; @include titleTipography;
@include text-ellipsis;
height: $s-32; height: $s-32;
padding: $s-8 0; padding: $s-8 0;
color: var(--assets-item-name-foreground-color-rest); color: var(--assets-item-name-foreground-color-rest);

View file

@ -93,7 +93,7 @@
} }
.opacity-wrapper { .opacity-wrapper {
background-color: var(--colorpicker-handlers-color); background-color: var(--colorpicker-background-color);
border-radius: $br-8; border-radius: $br-8;
} }

View file

@ -8,7 +8,6 @@
(:require-macros [app.main.style :as stl]) (:require-macros [app.main.style :as stl])
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
[app.main.data.workspace :as udw] [app.main.data.workspace :as udw]
[app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shape-layout :as dwsl]
@ -21,7 +20,8 @@
[app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]] [app.main.ui.workspace.sidebar.options.menus.layout-container :refer [get-layout-flex-icon]]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [tr]] [app.util.i18n :as i18n :refer [tr]]
[rumext.v2 :as mf])) [rumext.v2 :as mf]
[rumext.v2.props :as-alias mf.props]))
(def layout-item-attrs (def layout-item-attrs
[:layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0} [:layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0}
@ -36,246 +36,351 @@
:layout-item-absolute :layout-item-absolute
:layout-item-z-index]) :layout-item-z-index])
(mf/defc margin-section (defn- select-margins
[{:keys [values change-margin-style on-margin-change] :as props}] [m1? m2? m3? m4?]
(let [margin-type (or (:layout-item-margin-type values) :simple)
m1 (when (and (not (= :multiple (:layout-item-margin values)))
(= (dm/get-in values [:layout-item-margin :m1])
(dm/get-in values [:layout-item-margin :m3])))
(dm/get-in values [:layout-item-margin :m1]))
m2 (when (and (not (= :multiple (:layout-item-margin values)))
(= (dm/get-in values [:layout-item-margin :m2])
(dm/get-in values [:layout-item-margin :m4])))
(dm/get-in values [:layout-item-margin :m2]))
select-margins
(fn [m1? m2? m3? m4?]
(st/emit! (udw/set-margins-selected {:m1 m1? :m2 m2? :m3 m3? :m4 m4?}))) (st/emit! (udw/set-margins-selected {:m1 m1? :m2 m2? :m3 m3? :m4 m4?})))
select-margin #(select-margins (= % :m1) (= % :m2) (= % :m3) (= % :m4))] (defn- select-margin
[prop]
(select-margins (= prop :m1) (= prop :m2) (= prop :m3) (= prop :m4)))
(mf/defc margin-simple
{::mf/props :obj}
[{:keys [margin on-change on-blur]}]
(let [m1 (:m1 margin)
m2 (:m2 margin)
m3 (:m3 margin)
m4 (:m4 margin)
m1 (when (and (not= margin :multiple) (= m1 m3)) m1)
m2 (when (and (not= margin :multiple) (= m2 m4)) m2)
on-focus
(mf/use-fn
(fn [event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(case attr
:m1 (select-margins true false true false)
:m2 (select-margins false true false true))
(dom/select-target event))))
on-change'
(mf/use-fn
(mf/deps on-change)
(fn [value event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(on-change :simple attr value))))]
(mf/use-effect
(fn []
(fn []
;;on destroy component
(select-margins false false false false))))
[:div {:class (stl/css :margin-row)}
[:div {:class (stl/css :inputs-wrapper)}
(cond
(= margin-type :simple)
[:div {:class (stl/css :margin-simple)} [:div {:class (stl/css :margin-simple)}
[:div {:class (stl/css :vertical-margin) [:div {:class (stl/css :vertical-margin)
:title "Vertical margin"} :title "Vertical margin"}
[:span {:class (stl/css :icon)} [:span {:class (stl/css :icon)}
i/margin-top-bottom-refactor] i/margin-top-bottom-refactor]
[:> numeric-input* {:className (stl/css :numeric-input) [:> numeric-input* {:class (stl/css :numeric-input)
:placeholder "--" :placeholder "--"
:data-name "m1"
:on-focus on-focus
:on-change on-change'
:on-blur on-blur
:nillable true :nillable true
:value m1 :value m1}]]
:on-focus (fn [event]
(select-margins true false true false)
(dom/select-target event))
:on-change (partial on-margin-change :simple :m1)
:on-blur #(select-margins false false false false)}]]
[:div {:class (stl/css :horizontal-margin) [:div {:class (stl/css :horizontal-margin)
:title "Horizontal margin"} :title "Horizontal margin"}
[:span {:class (stl/css :icon)} [:span {:class (stl/css :icon)}
i/margin-left-right-refactor] i/margin-left-right-refactor]
[:> numeric-input* {:className (stl/css :numeric-input) [:> numeric-input* {:class (stl/css :numeric-input)
:placeholder "--" :placeholder "--"
:on-focus (fn [event] :data-name "m2"
(select-margins false true false true) :on-focus on-focus
(dom/select-target event)) :on-change on-change'
:on-change (partial on-margin-change :simple :m2) :on-blur on-blur
:on-blur #(select-margins false false false false)
:nillable true :nillable true
:value m2}]]] :value m2}]]]))
(mf/defc margin-multiple
{::mf/props :obj}
[{:keys [margin on-change on-blur]}]
(let [m1 (:m1 margin)
m2 (:m2 margin)
m3 (:m3 margin)
m4 (:m4 margin)
on-focus
(mf/use-fn
(fn [event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(select-margin attr)
(dom/select-target event))))
on-change'
(mf/use-fn
(mf/deps on-change)
(fn [value event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "name")
(keyword))]
(on-change :multiple attr value))))]
(= margin-type :multiple)
[:div {:class (stl/css :margin-multiple)} [:div {:class (stl/css :margin-multiple)}
[:div {:class (stl/css :top-margin) [:div {:class (stl/css :top-margin)
:title "Top margin"} :title "Top margin"}
[:span {:class (stl/css :icon)} [:span {:class (stl/css :icon)}
i/margin-top-refactor] i/margin-top-refactor]
[:> numeric-input* {:className (stl/css :numeric-input) [:> numeric-input* {:class (stl/css :numeric-input)
:placeholder "--" :placeholder "--"
:on-focus (fn [event] :data-name "m1"
(select-margin :m1) :on-focus on-focus
(dom/select-target event)) :on-change on-change'
:on-change (partial on-margin-change :multiple :m1) :on-blur on-blur
:on-blur #(select-margins false false false false)
:nillable true :nillable true
:value (:m1 (:layout-item-margin values))}]] :value m1}]]
[:div {:class (stl/css :right-margin) [:div {:class (stl/css :right-margin)
:title "Right margin"} :title "Right margin"}
[:span {:class (stl/css :icon)} [:span {:class (stl/css :icon)}
i/margin-right-refactor] i/margin-right-refactor]
[:> numeric-input* {:className (stl/css :numeric-input) [:> numeric-input* {:class (stl/css :numeric-input)
:placeholder "--" :placeholder "--"
:on-focus (fn [event] :data-name "m2"
(select-margin :m2) :on-focus on-focus
(dom/select-target event)) :on-change on-change'
:on-change (partial on-margin-change :multiple :m2) :on-blur on-blur
:on-blur #(select-margins false false false false)
:nillable true :nillable true
:value (:m2 (:layout-item-margin values))}]] :value m2}]]
[:div {:class (stl/css :bottom-margin) [:div {:class (stl/css :bottom-margin)
:title "Bottom margin"} :title "Bottom margin"}
[:span {:class (stl/css :icon)} [:span {:class (stl/css :icon)}
i/margin-bottom-refactor] i/margin-bottom-refactor]
[:> numeric-input* {:className (stl/css :numeric-input) [:> numeric-input* {:class (stl/css :numeric-input)
:placeholder "--" :placeholder "--"
:on-focus (fn [event] :data-name "m3"
(select-margin :m3) :on-focus on-focus
(dom/select-target event)) :on-change on-change'
:on-change (partial on-margin-change :multiple :m3) :on-blur on-blur
:on-blur #(select-margins false false false false)
:nillable true :nillable true
:value (:m3 (:layout-item-margin values))}]] :value m3}]]
[:div {:class (stl/css :left-margin) [:div {:class (stl/css :left-margin)
:title "Left margin"} :title "Left margin"}
[:span {:class (stl/css :icon)} [:span {:class (stl/css :icon)}
i/margin-left-refactor] i/margin-left-refactor]
[:> numeric-input* {:className (stl/css :numeric-input) [:> numeric-input* {:class (stl/css :numeric-input)
:placeholder "--" :placeholder "--"
:on-focus (fn [event] :data-name "m4"
(select-margin :m4) :on-focus on-focus
(dom/select-target event)) :on-change on-change'
:on-change (partial on-margin-change :multiple :m4) :on-blur on-blur
:on-blur #(select-margins false false false false)
:nillable true :nillable true
:value (:m4 (:layout-item-margin values))}]]])] :value m4}]]]))
[:button {:class (stl/css-case :margin-mode true
:selected (= margin-type :multiple))
(mf/defc margin-section
{::mf/props :obj
::mf/private true
::mf.props/expect #{:margin :type :on-type-change :on-change}}
[{:keys [type on-type-change] :as props}]
(let [type (d/nilv type :simple)
on-blur (mf/use-fn #(select-margins false false false false))
props (mf/spread-obj props {:on-blur on-blur})
on-type-change'
(mf/use-fn
(mf/deps type on-type-change)
(fn [_]
(if (= type :multiple)
(on-type-change :simple)
(on-type-change :multiple))))]
(mf/with-effect []
(fn [] (on-blur)))
[:div {:class (stl/css :margin-row)}
[:div {:class (stl/css :inputs-wrapper)}
(cond
(= type :simple)
[:> margin-simple props]
(= type :multiple)
[:> margin-multiple props])]
[:button {:class (stl/css-case
:margin-mode true
:selected (= type :multiple))
:title "Margin - multiple" :title "Margin - multiple"
:on-click #(change-margin-style (if (= margin-type :multiple) :simple :multiple))} :on-click on-type-change'}
i/margin-refactor]])) i/margin-refactor]]))
(mf/defc element-behaviour-horizontal (mf/defc element-behaviour-horizontal
[{:keys [auto? fill? layout-item-sizing on-change] :as props}] {::mf/props :obj}
[{:keys [^boolean is-auto ^boolean has-fill sizing on-change]}]
[:div {:class (stl/css-case :horizontal-behaviour true [:div {:class (stl/css-case :horizontal-behaviour true
:one-element (and (not fill?) (not auto?)) :one-element (and (not has-fill) (not is-auto))
:two-element (or fill? auto?) :two-element (or has-fill is-auto)
:three-element (and fill? auto?))} :three-element (and has-fill is-auto))}
[:& radio-buttons {:selected (d/name layout-item-sizing) [:& radio-buttons
{:selected (d/name sizing)
:on-change on-change :on-change on-change
:wide true :wide true
:name "flex-behaviour-h"} :name "flex-behaviour-h"}
[:& radio-button {:value "fix"
[:& radio-button
{:value "fix"
:icon i/fixed-width-refactor :icon i/fixed-width-refactor
:title "Fix width" :title "Fix width"
:id "behaviour-h-fix"}] :id "behaviour-h-fix"}]
(when fill?
[:& radio-button {:value "fill" (when has-fill
[:& radio-button
{:value "fill"
:icon i/fill-content-refactor :icon i/fill-content-refactor
:title "Width 100%" :title "Width 100%"
:id "behaviour-h-fill"}]) :id "behaviour-h-fill"}])
(when auto? (when is-auto
[:& radio-button {:value "auto" [:& radio-button
{:value "auto"
:icon i/hug-content-refactor :icon i/hug-content-refactor
:title "Fit content" :title "Fit content"
:id "behaviour-h-auto"}])]]) :id "behaviour-h-auto"}])]])
(mf/defc element-behaviour-vertical (mf/defc element-behaviour-vertical
[{:keys [auto? fill? layout-item-sizing on-change] :as props}] {::mf/props :obj}
[{:keys [^boolean is-auto ^boolean has-fill sizing on-change]}]
[:div {:class (stl/css-case :vertical-behaviour true [:div {:class (stl/css-case :vertical-behaviour true
:one-element (and (not fill?) (not auto?)) :one-element (and (not has-fill) (not is-auto))
:two-element (or fill? auto?) :two-element (or has-fill is-auto)
:three-element (and fill? auto?))} :three-element (and has-fill is-auto))}
[:& radio-buttons {:selected (d/name layout-item-sizing) [:& radio-buttons
{:selected (d/name sizing)
:on-change on-change :on-change on-change
:wide true :wide true
:name "flex-behaviour-v"} :name "flex-behaviour-v"}
[:& radio-button {:value "fix"
[:& radio-button
{:value "fix"
:icon i/fixed-width-refactor :icon i/fixed-width-refactor
:icon-class (stl/css :rotated) :icon-class (stl/css :rotated)
:title "Fix height" :title "Fix height"
:id "behaviour-v-fix"}] :id "behaviour-v-fix"}]
(when fill?
[:& radio-button {:value "fill" (when has-fill
[:& radio-button
{:value "fill"
:icon i/fill-content-refactor :icon i/fill-content-refactor
:icon-class (stl/css :rotated) :icon-class (stl/css :rotated)
:title "Height 100%" :title "Height 100%"
:id "behaviour-v-fill"}]) :id "behaviour-v-fill"}])
(when auto? (when is-auto
[:& radio-button {:value "auto" [:& radio-button
{:value "auto"
:icon i/hug-content-refactor :icon i/hug-content-refactor
:icon-class (stl/css :rotated) :icon-class (stl/css :rotated)
:title "Fit content" :title "Fit content"
:id "behaviour-v-auto"}])]]) :id "behaviour-v-auto"}])]])
(mf/defc element-behaviour (mf/defc element-behaviour
[{:keys [auto? {::mf/props :obj
fill? ::mf/private true}
layout-item-h-sizing [{:keys [^boolean is-auto
layout-item-v-sizing ^boolean has-fill
on-change-behaviour-h-refactor h-sizing
on-change-behaviour-v-refactor] :as props}] v-sizing
[:div {:class (stl/css-case :behaviour-menu true on-h-change
:wrap (and fill? auto?))} on-v-change]}]
[:& element-behaviour-horizontal {:auto? auto? [:div {:class (stl/css-case
:fill? fill? :behaviour-menu true
:layout-item-sizing layout-item-h-sizing :wrap (and has-fill is-auto))}
:on-change on-change-behaviour-h-refactor}]
[:& element-behaviour-vertical {:auto? auto? [:& element-behaviour-horizontal
:fill? fill? {:is-auto is-auto
:layout-item-sizing layout-item-v-sizing :has-fill has-fill
:on-change on-change-behaviour-v-refactor}]]) :sizing h-sizing
:on-change on-h-change}]
[:& element-behaviour-vertical
{:is-auto is-auto
:has-fill has-fill
:sizing v-sizing
:on-change on-v-change}]])
(mf/defc align-self-row (mf/defc align-self-row
[{:keys [is-col? align-self on-change] :as props}] {::mf/props :obj}
[{:keys [^boolean is-col align-self on-change]}]
[:& 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} :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"
:id "align-self-start"}] :id "align-self-start"}]
[:& radio-button {:value "center" [:& radio-button {:value "center"
:icon (get-layout-flex-icon :align-self :center is-col?) :icon (get-layout-flex-icon :align-self :center is-col)
:title "Align self center" :title "Align self center"
:id "align-self-center"}] :id "align-self-center"}]
[:& radio-button {:value "end" [:& radio-button {:value "end"
:icon (get-layout-flex-icon :align-self :end is-col?) :icon (get-layout-flex-icon :align-self :end is-col)
:title "Align self end" :title "Align self end"
:id "align-self-end"}]]) :id "align-self-end"}]])
(mf/defc layout-item-menu (mf/defc layout-item-menu
{::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "is-layout-child?" "is-grid-parent?" "is-flex-parent?"]))]} {::mf/memo #{:ids :values :type :is-layout-child? :is-grid-parent :is-flex-parent?}
[{:keys [ids values is-layout-child? is-layout-container? is-grid-parent? is-flex-parent? is-flex-layout? is-grid-layout?] :as props}] ::mf/props :obj}
[{:keys [ids values
^boolean is-layout-child?
^boolean is-layout-container?
^boolean is-grid-parent?
^boolean is-flex-parent?
^boolean is-flex-layout?
^boolean is-grid-layout?]}]
(let [selection-parents-ref (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) (let [selection-parents* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids))
selection-parents (mf/deref selection-parents-ref) selection-parents (mf/deref selection-parents*)
^boolean
is-absolute? (:layout-item-absolute values) is-absolute? (:layout-item-absolute values)
^boolean
is-col? (every? ctl/col? selection-parents) is-col? (every? ctl/col? selection-parents)
^boolean
is-layout-child? (and is-layout-child? (not is-absolute?)) is-layout-child? (and is-layout-child? (not is-absolute?))
state* (mf/use-state true) state* (mf/use-state true)
open? (deref state*) open? (deref state*)
toggle-content (mf/use-fn #(swap! state* not)) toggle-content (mf/use-fn #(swap! state* not))
has-content? (or is-layout-child? is-flex-parent? is-grid-parent? is-layout-container?) has-content? (or is-layout-child?
is-flex-parent?
is-grid-parent?
is-layout-container?)
;; Align self ;; Align self
align-self (:layout-item-align-self values) align-self (:layout-item-align-self values)
h-sizing (:layout-item-h-sizing values)
v-sizing (:layout-item-v-sizing values)
title title
(cond (cond
(and is-layout-container? (not is-layout-child?) is-flex-layout?) (and is-layout-container?
is-flex-layout?
(not is-layout-child?))
"Flex board" "Flex board"
(and is-layout-container? (not is-layout-child?) is-grid-layout?) (and is-layout-container?
is-grid-layout?
(not is-layout-child?))
"Grid board" "Grid board"
(and is-layout-container? (not is-layout-child?)) (and is-layout-container?
(not is-layout-child?))
"Layout board" "Layout board"
is-flex-parent? is-flex-parent?
@ -296,12 +401,15 @@
(st/emit! (dwsl/update-layout-child ids {:layout-item-align-self (keyword value)}))))) (st/emit! (dwsl/update-layout-child ids {:layout-item-align-self (keyword value)})))))
;; Margin ;; Margin
on-change-margin-type
change-margin-style (mf/use-fn
(mf/deps ids)
(fn [type] (fn [type]
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type}))) (st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type}))))
on-margin-change on-margin-change
(mf/use-fn
(mf/deps ids)
(fn [type prop val] (fn [type prop val]
(cond (cond
(and (= type :simple) (= prop :m1)) (and (= type :simple) (= prop :m1))
@ -311,24 +419,9 @@
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m2 val :m4 val}})) (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m2 val :m4 val}}))
:else :else
(st/emit! (dwsl/update-layout-child ids {:layout-item-margin {prop val}})))) (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {prop val}})))))
;; Behaviour ;; Behaviour
on-change-behaviour
(mf/use-fn
(mf/deps ids)
(fn [event]
(let [value (-> (dom/get-current-target event)
(dom/get-data "value")
(keyword))
dir (-> (dom/get-current-target event)
(dom/get-data "direction")
(keyword))]
(if (= dir :h)
(st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value}))
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value}))))))
on-change-behaviour-h on-change-behaviour-h
(mf/use-fn (mf/use-fn
(mf/deps ids) (mf/deps ids)
@ -336,7 +429,6 @@
(let [value (keyword value)] (let [value (keyword value)]
(st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value}))))) (st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value})))))
on-change-behaviour-v on-change-behaviour-v
(mf/use-fn (mf/use-fn
(mf/deps ids) (mf/deps ids)
@ -345,10 +437,14 @@
(st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value}))))) (st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value})))))
;; Size and position ;; Size and position
on-size-change on-size-change
(fn [measure value] (mf/use-fn
(st/emit! (dwsl/update-layout-child ids {measure value}))) (mf/deps ids)
(fn [value event]
(let [attr (-> (dom/get-current-target event)
(dom/get-data "attr")
(keyword))]
(st/emit! (dwsl/update-layout-child ids {attr value})))))
on-change-position on-change-position
(mf/use-fn (mf/use-fn
@ -391,10 +487,9 @@
[:div {:class (stl/css :z-index-wrapper) [:div {:class (stl/css :z-index-wrapper)
:title "z-index"} :title "z-index"}
[:span {:class (stl/css :icon-text)} [:span {:class (stl/css :icon-text)} "Z"]
"Z"]
[:> numeric-input* [:> numeric-input*
{:className (stl/css :numeric-input) {:class (stl/css :numeric-input)
:placeholder "--" :placeholder "--"
:on-focus #(dom/select-target %) :on-focus #(dom/select-target %)
:on-change #(on-change-z-index %) :on-change #(on-change-z-index %)
@ -402,28 +497,28 @@
:value (:layout-item-z-index values)}]]]) :value (:layout-item-z-index values)}]]])
[:div {:class (stl/css :row)} [:div {:class (stl/css :row)}
[:& element-behaviour {:fill? is-layout-child? [:& element-behaviour {:has-fill is-layout-child?
:auto? is-layout-container? :is-auto is-layout-container?
:layout-item-v-sizing (or (:layout-item-v-sizing values) :fix) :v-sizing (:layout-item-v-sizing values)
:layout-item-h-sizing (or (:layout-item-h-sizing values) :fix) :h-sizing (:layout-item-h-sizing values)
:on-change-behaviour-h-refactor on-change-behaviour-h :on-h-change on-change-behaviour-h
:on-change-behaviour-v-refactor on-change-behaviour-v :on-v-change on-change-behaviour-v}]]
:on-change on-change-behaviour}]]
(when (and is-layout-child? is-flex-parent?) (when (and is-layout-child? is-flex-parent?)
[:div {:class (stl/css :row)} [:div {:class (stl/css :row)}
[:& align-self-row {:is-col? is-col? [:& align-self-row {:is-col is-col?
:align-self align-self :align-self align-self
:on-change set-align-self}]]) :on-change set-align-self}]])
(when is-layout-child? (when is-layout-child?
[:div {:class (stl/css :row)} [:div {:class (stl/css :row)}
[:& margin-section {:values values [:& margin-section {:margin (:layout-item-margin values)
:change-margin-style change-margin-style :type (:layout-item-margin-type values)
:on-margin-change on-margin-change}]]) :on-type-change on-change-margin-type
:on-change on-margin-change}]])
(when (or (= (:layout-item-h-sizing values) :fill) (when (or (= h-sizing :fill)
(= (:layout-item-v-sizing values) :fill)) (= v-sizing :fill))
[:div {:class (stl/css :row)} [:div {:class (stl/css :row)}
[:div {:class (stl/css :advanced-options)} [:div {:class (stl/css :advanced-options)}
(when (= (:layout-item-h-sizing values) :fill) (when (= (:layout-item-h-sizing values) :fill)
@ -431,63 +526,64 @@
[:div {:class (stl/css :layout-item-min-w) [:div {:class (stl/css :layout-item-min-w)
:title (tr "workspace.options.layout-item.layout-item-min-w")} :title (tr "workspace.options.layout-item.layout-item-min-w")}
[:span {:class (stl/css :icon-text)} [:span {:class (stl/css :icon-text)} "MIN W"]
"MIN W"]
[:> numeric-input* [:> numeric-input*
{:className (stl/css :numeric-input) {:class (stl/css :numeric-input)
:no-validate true :no-validate true
:min 0 :min 0
:data-wrap true :data-wrap true
:placeholder "--" :placeholder "--"
:on-focus #(dom/select-target %) :data-attr "layout-item-min-w"
:on-change (partial on-size-change :layout-item-min-w) :on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-min-w) :value (get values :layout-item-min-w)
:nillable true}]] :nillable true}]]
[:div {:class (stl/css :layout-item-max-w) [:div {:class (stl/css :layout-item-max-w)
:title (tr "workspace.options.layout-item.layout-item-max-w")} :title (tr "workspace.options.layout-item.layout-item-max-w")}
[:span {:class (stl/css :icon-text)} [:span {:class (stl/css :icon-text)} "MAX W"]
"MAX W"]
[:> numeric-input* [:> numeric-input*
{:className (stl/css :numeric-input) {:class (stl/css :numeric-input)
:no-validate true :no-validate true
:min 0 :min 0
:data-wrap true :data-wrap true
:placeholder "--" :placeholder "--"
:on-focus #(dom/select-target %) :data-attr "layout-item-max-w"
:on-change (partial on-size-change :layout-item-max-w) :on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-max-w) :value (get values :layout-item-max-w)
:nillable true}]]]) :nillable true}]]])
(when (= (:layout-item-v-sizing values) :fill)
(when (= v-sizing :fill)
[:div {:class (stl/css :vertical-fill)} [:div {:class (stl/css :vertical-fill)}
[:div {:class (stl/css :layout-item-min-h) [:div {:class (stl/css :layout-item-min-h)
:title (tr "workspace.options.layout-item.layout-item-min-h")} :title (tr "workspace.options.layout-item.layout-item-min-h")}
[:span {:class (stl/css :icon-text)} [:span {:class (stl/css :icon-text)} "MIN H"]
"MIN H"]
[:> numeric-input* [:> numeric-input*
{:className (stl/css :numeric-input) {:class (stl/css :numeric-input)
:no-validate true :no-validate true
:min 0 :min 0
:data-wrap true :data-wrap true
:placeholder "--" :placeholder "--"
:on-focus #(dom/select-target %) :data-attr "layout-item-min-h"
:on-change (partial on-size-change :layout-item-min-h) :on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-min-h) :value (get values :layout-item-min-h)
:nillable true}]] :nillable true}]]
[:div {:class (stl/css :layout-item-max-h) [:div {:class (stl/css :layout-item-max-h)
:title (tr "workspace.options.layout-item.layout-item-max-h")} :title (tr "workspace.options.layout-item.layout-item-max-h")}
[:span {:class (stl/css :icon-text)} [:span {:class (stl/css :icon-text)} "MAX H"]
"MAX H"]
[:> numeric-input* [:> numeric-input*
{:className (stl/css :numeric-input) {:class (stl/css :numeric-input)
:no-validate true :no-validate true
:min 0 :min 0
:data-wrap true :data-wrap true
:placeholder "--" :placeholder "--"
:on-focus #(dom/select-target %) :data-attr "layout-item-max-h"
:on-change (partial on-size-change :layout-item-max-h) :on-focus dom/select-target
:on-change on-size-change
:value (get values :layout-item-max-h) :value (get values :layout-item-max-h)
:nillable true}]]])]])])])) :nillable true}]]])]])])]))

View file

@ -20,15 +20,13 @@
.color-info { .color-info {
--detach-icon-foreground-color: none; --detach-icon-foreground-color: none;
display: flex; display: grid;
grid-template-columns: 1fr auto;
align-items: center; align-items: center;
gap: $s-2; gap: $s-2;
border-radius: $s-8; border-radius: $s-8;
background-color: var(--input-details-color); background-color: var(--input-details-color);
height: $s-32; height: $s-32;
width: 100%;
flex-grow: 1;
min-width: 0;
&:hover { &:hover {
--detach-icon-foreground-color: var(--input-foreground-color-active); --detach-icon-foreground-color: var(--input-foreground-color-active);