diff --git a/backend/scripts/repl b/backend/scripts/repl index f521f2bff..4e454b761 100755 --- a/backend/scripts/repl +++ b/backend/scripts/repl @@ -36,6 +36,9 @@ export PENPOT_FLAGS="\ # Setup default upload media file size to 100MiB 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_USERNAME="penpot" # export PENPOT_DATABASE_PASSWORD="penpot" diff --git a/backend/scripts/start-dev b/backend/scripts/start-dev index 8fecd79af..89df83d96 100755 --- a/backend/scripts/start-dev +++ b/backend/scripts/start-dev @@ -37,6 +37,9 @@ export OPTIONS=" # Setup default upload media file size to 100MiB 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 # export OPTIONS="$OPTIONS -J-Xms50m -J-Xmx1024m" # export OPTIONS="$OPTIONS -J-Xms1100m -J-Xmx1100m -J-XX:+AlwaysPreTouch" diff --git a/backend/src/app/binfile/v1.clj b/backend/src/app/binfile/v1.clj index 8597254c7..d2b7cdf7f 100644 --- a/backend/src/app/binfile/v1.clj +++ b/backend/src/app/binfile/v1.clj @@ -65,7 +65,11 @@ (def ^:const buffer-size (:xnio/buffer-size yt/defaults)) (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) diff --git a/backend/src/app/http.clj b/backend/src/app/http.clj index 599225827..1e605cdb0 100644 --- a/backend/src/app/http.clj +++ b/backend/src/app/http.clj @@ -52,8 +52,8 @@ [_ cfg] (merge {::port 6060 ::host "0.0.0.0" - ::max-body-size (* 1024 1024 30) ; 30 MiB - ::max-multipart-body-size (* 1024 1024 120)} ; 120 MiB + ::max-body-size (* 1024 1024 30) ; default 30 MiB + ::max-multipart-body-size (* 1024 1024 120)} ; default 120 MiB (d/without-nils cfg))) (defmethod ig/pre-init-spec ::server [_] diff --git a/backend/src/app/rpc/commands/files.clj b/backend/src/app/rpc/commands/files.clj index 368760f9e..b2a97c6cc 100644 --- a/backend/src/app/rpc/commands/files.clj +++ b/backend/src/app/rpc/commands/files.clj @@ -347,6 +347,7 @@ (sv/defmethod ::get-file-fragment "Retrieve a file fragment by its ID. Only authenticated users." {::doc/added "1.17" + ::rpc/auth false ::sm/params schema:get-file-fragment ::sm/result schema:file-fragment} [{:keys [::db/pool] :as cfg} {:keys [::rpc/profile-id file-id fragment-id share-id]}] diff --git a/common/src/app/common/geom/proportions.cljc b/common/src/app/common/geom/proportions.cljc index d6f37c216..7afaeef4d 100644 --- a/common/src/app/common/geom/proportions.cljc +++ b/common/src/app/common/geom/proportions.cljc @@ -40,6 +40,6 @@ (cond (= type :svg-raw) (setup-proportions-size shape) (= type :image) (setup-proportions-image shape) - image-fill? (setup-proportions-size shape) (= type :text) shape + image-fill? (setup-proportions-size shape) :else (setup-proportions-const shape)))) diff --git a/common/src/app/common/geom/shapes/bounds.cljc b/common/src/app/common/geom/shapes/bounds.cljc index c60840d7a..854d24440 100644 --- a/common/src/app/common/geom/shapes/bounds.cljc +++ b/common/src/app/common/geom/shapes/bounds.cljc @@ -15,8 +15,8 @@ (defn shape-stroke-margin [shape stroke-width] (if (cfh/path-shape? shape) - ;; TODO: Calculate with the stroke offset (not implemented yet - (mth/sqrt (* 2 stroke-width stroke-width)) + ;; TODO: Calculate with the stroke offset (not implemented yet) + (+ stroke-width (mth/sqrt (* 2 stroke-width stroke-width))) (- (mth/sqrt (* 2 stroke-width stroke-width)) stroke-width))) (defn- apply-filters @@ -104,7 +104,7 @@ 0)) (reduce d/max 0)) - margin + stroke-margin (if ignore-margin? 0 (shape-stroke-margin shape stroke-width)) @@ -124,9 +124,8 @@ :drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10) 0)) (reduce d/max 0))] - - {:horizontal (mth/ceil (+ stroke-width margin shadow-width)) - :vertical (mth/ceil (+ stroke-width margin shadow-height))}))) + {:horizontal (mth/ceil (+ stroke-margin shadow-width)) + :vertical (mth/ceil (+ stroke-margin shadow-height))}))) (defn- add-padding [bounds padding] @@ -143,47 +142,51 @@ (update :height + (* 2 v-padding))))) (defn calculate-base-bounds - [shape] - (-> (get-shape-filter-bounds shape) - (add-padding (calculate-padding shape true)))) + ([shape] + (calculate-base-bounds shape true)) + ([shape ignore-margin?] + (-> (get-shape-filter-bounds shape) + (add-padding (calculate-padding shape ignore-margin?))))) (defn get-object-bounds - [objects shape] - (let [base-bounds (calculate-base-bounds shape) - bounds - (cond - (or (empty? (:shapes shape)) - (or (:masked-group shape) (= :bool (:type shape))) - (and (cfh/frame-shape? shape) (not (:show-content shape)))) - [base-bounds] + ([objects 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 + (cond + (or (empty? (:shapes shape)) + (or (:masked-group shape) (= :bool (:type shape))) + (and (cfh/frame-shape? shape) (not (:show-content shape)))) + [base-bounds] - :else - (cfh/reduce-objects - objects + :else + (cfh/reduce-objects + objects - (fn [shape] - (and (not (:hidden shape)) - (d/not-empty? (:shapes shape)) - (or (not (cfh/frame-shape? shape)) - (:show-content shape)) + (fn [shape] + (and (not (:hidden shape)) + (d/not-empty? (:shapes shape)) + (or (not (cfh/frame-shape? shape)) + (:show-content shape)) - (or (not (cfh/group-shape? shape)) - (not (:masked-group shape))))) - (:id shape) + (or (not (cfh/group-shape? shape)) + (not (:masked-group shape))))) + (:id shape) - (fn [result child] - (cond-> result - (not (:hidden child)) - (conj (calculate-base-bounds child)))) + (fn [result child] + (cond-> result + (not (:hidden child)) + (conj (calculate-base-bounds child)))) - [base-bounds])) + [base-bounds])) - children-bounds - (cond->> (grc/join-rects bounds) - (not (cfh/frame-shape? shape)) (or (:children-bounds shape))) + children-bounds + (cond->> (grc/join-rects bounds) + (not (cfh/frame-shape? shape)) (or (:children-bounds shape))) - filters (shape->filters shape) - blur-value (or (-> shape :blur :value) 0)] + filters (shape->filters shape) + 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)))) diff --git a/docker/devenv/Dockerfile b/docker/devenv/Dockerfile index ef47e54b6..5e1aaffcb 100644 --- a/docker/devenv/Dockerfile +++ b/docker/devenv/Dockerfile @@ -3,10 +3,10 @@ LABEL maintainer="Andrey Antukh " ARG DEBIAN_FRONTEND=noninteractive -ENV NODE_VERSION=v20.10.0 \ - CLOJURE_VERSION=1.11.1.1429 \ - CLJKONDO_VERSION=2023.12.15 \ - BABASHKA_VERSION=1.3.187 \ +ENV NODE_VERSION=v20.11.1 \ + CLOJURE_VERSION=1.11.1.1435 \ + CLJKONDO_VERSION=2024.02.12 \ + BABASHKA_VERSION=1.3.188 \ CLJFMT_VERSION=0.12.0 \ LANG=en_US.UTF-8 \ LC_ALL=en_US.UTF-8 @@ -105,12 +105,12 @@ RUN set -eux; \ ARCH="$(dpkg --print-architecture)"; \ case "${ARCH}" in \ aarch64|arm64) \ - ESUM='aa43295803595f78d73e9c7c02866301c9729377277144e2829f54a58e5f6d21'; \ - BINARY_URL='https://corretto.aws/downloads/resources/21.0.1.12.1/amazon-corretto-21.0.1.12.1-linux-aarch64.tar.gz'; \ + ESUM='3ce6a2b357e2ef45fd6b53d6587aa05bfec7771e7fb982f2c964f6b771b7526a'; \ + 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) \ - ESUM='3e718a86cfa6c1173c469f5e9d6b07fa37381a28ebb1f80593250cc380baf22f'; \ - BINARY_URL='https://corretto.aws/downloads/resources/21.0.1.12.1/amazon-corretto-21.0.1.12.1-linux-x64.tar.gz'; \ + ESUM='454bebb2c9fe48d981341461ffb6bf1017c7b7c6e15c6b0c29b959194ba3aaa5'; \ + 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}"; \ diff --git a/docker/devenv/files/nginx.conf b/docker/devenv/files/nginx.conf index d291512c8..24a0f2ddf 100644 --- a/docker/devenv/files/nginx.conf +++ b/docker/devenv/files/nginx.conf @@ -49,7 +49,7 @@ http { listen 3449 default_server; server_name _; - client_max_body_size 100M; + client_max_body_size 300M; charset utf-8; proxy_http_version 1.1; diff --git a/frontend/deps.edn b/frontend/deps.edn index 9e686a3f1..a626ecbd9 100644 --- a/frontend/deps.edn +++ b/frontend/deps.edn @@ -19,8 +19,8 @@ :git/url "https://github.com/funcool/beicon.git"} funcool/rumext - {:git/tag "v2.9.4" - :git/sha "af08e55" + {:git/tag "v2.10" + :git/sha "d96ea18" :git/url "https://github.com/funcool/rumext.git"} instaparse/instaparse {:mvn/version "1.4.12"} diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index 799195f84..f9c6f84b6 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -273,6 +273,7 @@ --colorpicker-details-color: var(--color-background-quaternary); --colorpicker-details-color-selected: var(--color-accent-primary); --colorpicker-handlers-color: var(--color-foreground-primary); + --colorpicker-background-color: var(--color-foreground-primary); // COMMENTS --comment-title-color: var(--color-foreground-primary); @@ -400,4 +401,6 @@ --text-editor-selection-background-color: var(--la-tertiary-70); --expand-button-icon-border-width-selected: 2px; + + --colorpicker-background-color: var(--color-background-primary); } diff --git a/frontend/src/app/main/data/viewer.cljs b/frontend/src/app/main/data/viewer.cljs index af8401d8a..a698e567c 100644 --- a/frontend/src/app/main/data/viewer.cljs +++ b/frontend/src/app/main/data/viewer.cljs @@ -74,9 +74,11 @@ (assoc-in [:viewer-local :interactions-show?] interactions-show?))) ptk/WatchEvent - (watch [_ _ _] + (watch [_ state _] (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 (effect [_ _ _] diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 0249bf2ca..31c2f20be 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1618,7 +1618,7 @@ file-id (:current-file-id state) 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 :features features diff --git a/frontend/src/app/main/data/workspace/interactions.cljs b/frontend/src/app/main/data/workspace/interactions.cljs index a8e2fe284..2bdc9f2e1 100644 --- a/frontend/src/app/main/data/workspace/interactions.cljs +++ b/frontend/src/app/main/data/workspace/interactions.cljs @@ -208,7 +208,10 @@ from-frame-id (if (cfh/frame-shape? 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) (not= (:id target-frame) from-frame-id)) diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 0a19c8a84..be312cae2 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -449,7 +449,7 @@ (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) fonts (ff/shape->fonts object objects) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index 6366c42e0..e855405a1 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -47,7 +47,7 @@ :center (/ (:stroke-width stroke 0) 2) :outer (:stroke-width stroke 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 ;; dependencies to really useful shape attrs instead of using @@ -57,8 +57,6 @@ (gst/shape->rect shape) (grc/points->rect (:points shape)))) - stroke-margin (+ stroke-width margin) - x (- (dm/get-prop selrect :x) stroke-margin) y (- (dm/get-prop selrect :y) stroke-margin) w (+ (dm/get-prop selrect :width) (* 2 stroke-margin)) diff --git a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss index 5d9646ab6..2610509b9 100644 --- a/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss +++ b/frontend/src/app/main/ui/viewer/inspect/right_sidebar.scss @@ -28,7 +28,8 @@ } .shape-row { - display: flex; + display: grid; + grid-template-columns: auto 1fr; gap: $s-8; align-items: center; height: $s-32; @@ -46,6 +47,7 @@ .layer-title { @include titleTipography; + @include text-ellipsis; height: $s-32; padding: $s-8 0; color: var(--assets-item-name-foreground-color-rest); diff --git a/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.scss b/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.scss index 65d2ae287..aef415df5 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.scss +++ b/frontend/src/app/main/ui/workspace/colorpicker/slider_selector.scss @@ -93,7 +93,7 @@ } .opacity-wrapper { - background-color: var(--colorpicker-handlers-color); + background-color: var(--colorpicker-background-color); border-radius: $br-8; } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs index 6929eb505..dff99e0a9 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_item.cljs @@ -8,7 +8,6 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] - [app.common.data.macros :as dm] [app.common.types.shape.layout :as ctl] [app.main.data.workspace :as udw] [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.util.dom :as dom] [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 [:layout-item-margin ;; {:m1 0 :m2 0 :m3 0 :m4 0} @@ -36,246 +36,351 @@ :layout-item-absolute :layout-item-z-index]) +(defn- select-margins + [m1? m2? m3? m4?] + (st/emit! (udw/set-margins-selected {:m1 m1? :m2 m2? :m3 m3? :m4 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))))] + + + [:div {:class (stl/css :margin-simple)} + [:div {:class (stl/css :vertical-margin) + :title "Vertical margin"} + [:span {:class (stl/css :icon)} + i/margin-top-bottom-refactor] + [:> numeric-input* {:class (stl/css :numeric-input) + :placeholder "--" + :data-name "m1" + :on-focus on-focus + :on-change on-change' + :on-blur on-blur + :nillable true + :value m1}]] + + [:div {:class (stl/css :horizontal-margin) + :title "Horizontal margin"} + [:span {:class (stl/css :icon)} + i/margin-left-right-refactor] + [:> numeric-input* {:class (stl/css :numeric-input) + :placeholder "--" + :data-name "m2" + :on-focus on-focus + :on-change on-change' + :on-blur on-blur + :nillable true + :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))))] + + [:div {:class (stl/css :margin-multiple)} + [:div {:class (stl/css :top-margin) + :title "Top margin"} + [:span {:class (stl/css :icon)} + i/margin-top-refactor] + [:> numeric-input* {:class (stl/css :numeric-input) + :placeholder "--" + :data-name "m1" + :on-focus on-focus + :on-change on-change' + :on-blur on-blur + :nillable true + :value m1}]] + [:div {:class (stl/css :right-margin) + :title "Right margin"} + [:span {:class (stl/css :icon)} + i/margin-right-refactor] + [:> numeric-input* {:class (stl/css :numeric-input) + :placeholder "--" + :data-name "m2" + :on-focus on-focus + :on-change on-change' + :on-blur on-blur + :nillable true + :value m2}]] + + [:div {:class (stl/css :bottom-margin) + :title "Bottom margin"} + [:span {:class (stl/css :icon)} + i/margin-bottom-refactor] + [:> numeric-input* {:class (stl/css :numeric-input) + :placeholder "--" + :data-name "m3" + :on-focus on-focus + :on-change on-change' + :on-blur on-blur + :nillable true + :value m3}]] + + [:div {:class (stl/css :left-margin) + :title "Left margin"} + [:span {:class (stl/css :icon)} + i/margin-left-refactor] + [:> numeric-input* {:class (stl/css :numeric-input) + :placeholder "--" + :data-name "m4" + :on-focus on-focus + :on-change on-change' + :on-blur on-blur + :nillable true + :value m4}]]])) + + (mf/defc margin-section - [{:keys [values change-margin-style on-margin-change] :as props}] + {::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}) - (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])) + on-type-change' + (mf/use-fn + (mf/deps type on-type-change) + (fn [_] + (if (= type :multiple) + (on-type-change :simple) + (on-type-change :multiple))))] - 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?}))) - - select-margin #(select-margins (= % :m1) (= % :m2) (= % :m3) (= % :m4))] - - (mf/use-effect - (fn [] - (fn [] - ;;on destroy component - (select-margins false false false false)))) + (mf/with-effect [] + (fn [] (on-blur))) [: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 :vertical-margin) - :title "Vertical margin"} - [:span {:class (stl/css :icon)} - i/margin-top-bottom-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :nillable true - :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)}]] + (= type :simple) + [:> margin-simple props] - [:div {:class (stl/css :horizontal-margin) - :title "Horizontal margin"} - [:span {:class (stl/css :icon)} - i/margin-left-right-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margins false true false true) - (dom/select-target event)) - :on-change (partial on-margin-change :simple :m2) - :on-blur #(select-margins false false false false) - :nillable true - :value m2}]]] + (= type :multiple) + [:> margin-multiple props])] - (= margin-type :multiple) - [:div {:class (stl/css :margin-multiple)} - [:div {:class (stl/css :top-margin) - :title "Top margin"} - [:span {:class (stl/css :icon)} - i/margin-top-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m1) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m1) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m1 (:layout-item-margin values))}]] - [:div {:class (stl/css :right-margin) - :title "Right margin"} - [:span {:class (stl/css :icon)} - i/margin-right-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m2) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m2) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m2 (:layout-item-margin values))}]] - - [:div {:class (stl/css :bottom-margin) - :title "Bottom margin"} - [:span {:class (stl/css :icon)} - i/margin-bottom-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m3) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m3) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m3 (:layout-item-margin values))}]] - [:div {:class (stl/css :left-margin) - :title "Left margin"} - [:span {:class (stl/css :icon)} - i/margin-left-refactor] - [:> numeric-input* {:className (stl/css :numeric-input) - :placeholder "--" - :on-focus (fn [event] - (select-margin :m4) - (dom/select-target event)) - :on-change (partial on-margin-change :multiple :m4) - :on-blur #(select-margins false false false false) - :nillable true - :value (:m4 (:layout-item-margin values))}]]])] - [:button {:class (stl/css-case :margin-mode true - :selected (= margin-type :multiple)) + [:button {:class (stl/css-case + :margin-mode true + :selected (= type :multiple)) :title "Margin - multiple" - :on-click #(change-margin-style (if (= margin-type :multiple) :simple :multiple))} + :on-click on-type-change'} i/margin-refactor]])) (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 - :one-element (and (not fill?) (not auto?)) - :two-element (or fill? auto?) - :three-element (and fill? auto?))} - [:& radio-buttons {:selected (d/name layout-item-sizing) - :on-change on-change - :wide true - :name "flex-behaviour-h"} - [:& radio-button {:value "fix" - :icon i/fixed-width-refactor - :title "Fix width" - :id "behaviour-h-fix"}] - (when fill? - [:& radio-button {:value "fill" - :icon i/fill-content-refactor - :title "Width 100%" - :id "behaviour-h-fill"}]) - (when auto? - [:& radio-button {:value "auto" - :icon i/hug-content-refactor - :title "Fit content" - :id "behaviour-h-auto"}])]]) + :one-element (and (not has-fill) (not is-auto)) + :two-element (or has-fill is-auto) + :three-element (and has-fill is-auto))} + [:& radio-buttons + {:selected (d/name sizing) + :on-change on-change + :wide true + :name "flex-behaviour-h"} + + [:& radio-button + {:value "fix" + :icon i/fixed-width-refactor + :title "Fix width" + :id "behaviour-h-fix"}] + + (when has-fill + [:& radio-button + {:value "fill" + :icon i/fill-content-refactor + :title "Width 100%" + :id "behaviour-h-fill"}]) + (when is-auto + [:& radio-button + {:value "auto" + :icon i/hug-content-refactor + :title "Fit content" + :id "behaviour-h-auto"}])]]) (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 - :one-element (and (not fill?) (not auto?)) - :two-element (or fill? auto?) - :three-element (and fill? auto?))} - [:& radio-buttons {:selected (d/name layout-item-sizing) - :on-change on-change - :wide true - :name "flex-behaviour-v"} - [:& radio-button {:value "fix" - :icon i/fixed-width-refactor - :icon-class (stl/css :rotated) - :title "Fix height" - :id "behaviour-v-fix"}] - (when fill? - [:& radio-button {:value "fill" - :icon i/fill-content-refactor - :icon-class (stl/css :rotated) - :title "Height 100%" - :id "behaviour-v-fill"}]) - (when auto? - [:& radio-button {:value "auto" - :icon i/hug-content-refactor - :icon-class (stl/css :rotated) - :title "Fit content" - :id "behaviour-v-auto"}])]]) + :one-element (and (not has-fill) (not is-auto)) + :two-element (or has-fill is-auto) + :three-element (and has-fill is-auto))} + [:& radio-buttons + {:selected (d/name sizing) + :on-change on-change + :wide true + :name "flex-behaviour-v"} + + [:& radio-button + {:value "fix" + :icon i/fixed-width-refactor + :icon-class (stl/css :rotated) + :title "Fix height" + :id "behaviour-v-fix"}] + + (when has-fill + [:& radio-button + {:value "fill" + :icon i/fill-content-refactor + :icon-class (stl/css :rotated) + :title "Height 100%" + :id "behaviour-v-fill"}]) + (when is-auto + [:& radio-button + {:value "auto" + :icon i/hug-content-refactor + :icon-class (stl/css :rotated) + :title "Fit content" + :id "behaviour-v-auto"}])]]) (mf/defc element-behaviour - [{:keys [auto? - fill? - layout-item-h-sizing - layout-item-v-sizing - on-change-behaviour-h-refactor - on-change-behaviour-v-refactor] :as props}] - [:div {:class (stl/css-case :behaviour-menu true - :wrap (and fill? auto?))} - [:& element-behaviour-horizontal {:auto? auto? - :fill? fill? - :layout-item-sizing layout-item-h-sizing - :on-change on-change-behaviour-h-refactor}] - [:& element-behaviour-vertical {:auto? auto? - :fill? fill? - :layout-item-sizing layout-item-v-sizing - :on-change on-change-behaviour-v-refactor}]]) + {::mf/props :obj + ::mf/private true} + [{:keys [^boolean is-auto + ^boolean has-fill + h-sizing + v-sizing + on-h-change + on-v-change]}] + [:div {:class (stl/css-case + :behaviour-menu true + :wrap (and has-fill is-auto))} + + [:& element-behaviour-horizontal + {:is-auto is-auto + :has-fill has-fill + :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 - [{: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) :on-change on-change :name "flex-align-self" :allow-empty true} [:& 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" :id "align-self-start"}] [:& 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" :id "align-self-center"}] [:& 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" :id "align-self-end"}]]) (mf/defc layout-item-menu - {::mf/wrap [#(mf/memo' % (mf/check-props ["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/memo #{:ids :values :type :is-layout-child? :is-grid-parent :is-flex-parent?} + ::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)) - selection-parents (mf/deref selection-parents-ref) + (let [selection-parents* (mf/use-memo (mf/deps ids) #(refs/parents-by-ids ids)) + selection-parents (mf/deref selection-parents*) - is-absolute? (:layout-item-absolute values) + ^boolean + is-absolute? (:layout-item-absolute values) - is-col? (every? ctl/col? selection-parents) + ^boolean + is-col? (every? ctl/col? selection-parents) - is-layout-child? (and is-layout-child? (not is-absolute?)) + ^boolean + is-layout-child? (and is-layout-child? (not is-absolute?)) - state* (mf/use-state true) - open? (deref state*) - toggle-content (mf/use-fn #(swap! state* not)) - has-content? (or is-layout-child? is-flex-parent? is-grid-parent? is-layout-container?) + state* (mf/use-state true) + open? (deref state*) + + toggle-content (mf/use-fn #(swap! state* not)) + has-content? (or is-layout-child? + is-flex-parent? + is-grid-parent? + is-layout-container?) ;; Align self - align-self (:layout-item-align-self values) + h-sizing (:layout-item-h-sizing values) + v-sizing (:layout-item-v-sizing values) title (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" - (and is-layout-container? (not is-layout-child?) is-grid-layout?) + (and is-layout-container? + is-grid-layout? + (not is-layout-child?)) "Grid board" - (and is-layout-container? (not is-layout-child?)) + (and is-layout-container? + (not is-layout-child?)) "Layout board" is-flex-parent? @@ -296,39 +401,27 @@ (st/emit! (dwsl/update-layout-child ids {:layout-item-align-self (keyword value)}))))) ;; Margin - - change-margin-style - (fn [type] - (st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type}))) - - on-margin-change - (fn [type prop val] - (cond - (and (= type :simple) (= prop :m1)) - (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m1 val :m3 val}})) - - (and (= type :simple) (= prop :m2)) - (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m2 val :m4 val}})) - - :else - (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {prop val}})))) - - ;; Behaviour - - on-change-behaviour + on-change-margin-type (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})))))) + (fn [type] + (st/emit! (dwsl/update-layout-child ids {:layout-item-margin-type type})))) + on-margin-change + (mf/use-fn + (mf/deps ids) + (fn [type prop val] + (cond + (and (= type :simple) (= prop :m1)) + (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m1 val :m3 val}})) + + (and (= type :simple) (= prop :m2)) + (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {:m2 val :m4 val}})) + + :else + (st/emit! (dwsl/update-layout-child ids {:layout-item-margin {prop val}}))))) + + ;; Behaviour on-change-behaviour-h (mf/use-fn (mf/deps ids) @@ -336,7 +429,6 @@ (let [value (keyword value)] (st/emit! (dwsl/update-layout-child ids {:layout-item-h-sizing value}))))) - on-change-behaviour-v (mf/use-fn (mf/deps ids) @@ -345,10 +437,14 @@ (st/emit! (dwsl/update-layout-child ids {:layout-item-v-sizing value}))))) ;; Size and position - on-size-change - (fn [measure value] - (st/emit! (dwsl/update-layout-child ids {measure value}))) + (mf/use-fn + (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 (mf/use-fn @@ -391,10 +487,9 @@ [:div {:class (stl/css :z-index-wrapper) :title "z-index"} - [:span {:class (stl/css :icon-text)} - "Z"] + [:span {:class (stl/css :icon-text)} "Z"] [:> numeric-input* - {:className (stl/css :numeric-input) + {:class (stl/css :numeric-input) :placeholder "--" :on-focus #(dom/select-target %) :on-change #(on-change-z-index %) @@ -402,28 +497,28 @@ :value (:layout-item-z-index values)}]]]) [:div {:class (stl/css :row)} - [:& element-behaviour {:fill? is-layout-child? - :auto? is-layout-container? - :layout-item-v-sizing (or (:layout-item-v-sizing values) :fix) - :layout-item-h-sizing (or (:layout-item-h-sizing values) :fix) - :on-change-behaviour-h-refactor on-change-behaviour-h - :on-change-behaviour-v-refactor on-change-behaviour-v - :on-change on-change-behaviour}]] + [:& element-behaviour {:has-fill is-layout-child? + :is-auto is-layout-container? + :v-sizing (:layout-item-v-sizing values) + :h-sizing (:layout-item-h-sizing values) + :on-h-change on-change-behaviour-h + :on-v-change on-change-behaviour-v}]] (when (and is-layout-child? is-flex-parent?) [:div {:class (stl/css :row)} - [:& align-self-row {:is-col? is-col? + [:& align-self-row {:is-col is-col? :align-self align-self :on-change set-align-self}]]) (when is-layout-child? [:div {:class (stl/css :row)} - [:& margin-section {:values values - :change-margin-style change-margin-style - :on-margin-change on-margin-change}]]) + [:& margin-section {:margin (:layout-item-margin values) + :type (:layout-item-margin-type values) + :on-type-change on-change-margin-type + :on-change on-margin-change}]]) - (when (or (= (:layout-item-h-sizing values) :fill) - (= (:layout-item-v-sizing values) :fill)) + (when (or (= h-sizing :fill) + (= v-sizing :fill)) [:div {:class (stl/css :row)} [:div {:class (stl/css :advanced-options)} (when (= (:layout-item-h-sizing values) :fill) @@ -431,63 +526,64 @@ [:div {:class (stl/css :layout-item-min-w) :title (tr "workspace.options.layout-item.layout-item-min-w")} - [:span {:class (stl/css :icon-text)} - "MIN W"] + [:span {:class (stl/css :icon-text)} "MIN W"] [:> numeric-input* - {:className (stl/css :numeric-input) + {:class (stl/css :numeric-input) :no-validate true :min 0 :data-wrap true :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-min-w) + :data-attr "layout-item-min-w" + :on-focus dom/select-target + :on-change on-size-change :value (get values :layout-item-min-w) :nillable true}]] [:div {:class (stl/css :layout-item-max-w) :title (tr "workspace.options.layout-item.layout-item-max-w")} - [:span {:class (stl/css :icon-text)} - "MAX W"] + [:span {:class (stl/css :icon-text)} "MAX W"] [:> numeric-input* - {:className (stl/css :numeric-input) + {:class (stl/css :numeric-input) :no-validate true :min 0 :data-wrap true :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-max-w) + :data-attr "layout-item-max-w" + :on-focus dom/select-target + :on-change on-size-change :value (get values :layout-item-max-w) :nillable true}]]]) - (when (= (:layout-item-v-sizing values) :fill) + + (when (= v-sizing :fill) [:div {:class (stl/css :vertical-fill)} [:div {:class (stl/css :layout-item-min-h) :title (tr "workspace.options.layout-item.layout-item-min-h")} - [:span {:class (stl/css :icon-text)} - "MIN H"] + [:span {:class (stl/css :icon-text)} "MIN H"] [:> numeric-input* - {:className (stl/css :numeric-input) + {:class (stl/css :numeric-input) :no-validate true :min 0 :data-wrap true :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-min-h) + :data-attr "layout-item-min-h" + :on-focus dom/select-target + :on-change on-size-change :value (get values :layout-item-min-h) :nillable true}]] [:div {:class (stl/css :layout-item-max-h) :title (tr "workspace.options.layout-item.layout-item-max-h")} - [:span {:class (stl/css :icon-text)} - "MAX H"] + [:span {:class (stl/css :icon-text)} "MAX H"] [:> numeric-input* - {:className (stl/css :numeric-input) + {:class (stl/css :numeric-input) :no-validate true :min 0 :data-wrap true :placeholder "--" - :on-focus #(dom/select-target %) - :on-change (partial on-size-change :layout-item-max-h) + :data-attr "layout-item-max-h" + :on-focus dom/select-target + :on-change on-size-change :value (get values :layout-item-max-h) :nillable true}]]])]])])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss index f02cf1aeb..0eb672b73 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/color_row.scss @@ -20,15 +20,13 @@ .color-info { --detach-icon-foreground-color: none; - display: flex; + display: grid; + grid-template-columns: 1fr auto; align-items: center; gap: $s-2; border-radius: $s-8; background-color: var(--input-details-color); height: $s-32; - width: 100%; - flex-grow: 1; - min-width: 0; &:hover { --detach-icon-foreground-color: var(--input-foreground-color-active);