diff --git a/frontend/src/app/main/fonts.cljs b/frontend/src/app/main/fonts.cljs index d563da84e..5b06f449b 100644 --- a/frontend/src/app/main/fonts.cljs +++ b/frontend/src/app/main/fonts.cljs @@ -242,8 +242,8 @@ (defn ready [cb] - (-> (obj/get-in js/document ["fonts" "ready"]) - (p/then cb))) + (let [fonts (obj/get js/document "fonts")] + (p/then (obj/get fonts "ready") cb))) (defn get-default-variant [{:keys [variants]}] diff --git a/frontend/src/app/main/ui/components/dropdown_menu.cljs b/frontend/src/app/main/ui/components/dropdown_menu.cljs index 156a1b651..8f9daef57 100644 --- a/frontend/src/app/main/ui/components/dropdown_menu.cljs +++ b/frontend/src/app/main/ui/components/dropdown_menu.cljs @@ -96,14 +96,17 @@ [:ul {:class list-class :role "menu"} children])) (mf/defc dropdown-menu - {::mf/wrap-props false} + {::mf/props :obj} [props] (assert (fn? (gobj/get props "on-close")) "missing `on-close` prop") (assert (boolean? (gobj/get props "show")) "missing `show` prop") (let [ids (obj/get props "ids") - ids (d/nilv ids (->> (obj/get props "children") - (keep #(obj/get-in % ["props" "id"]))))] + ids (or ids + (->> (obj/get props "children") + (keep (fn [o] + (let [props (obj/get o "props")] + (obj/get props "id"))))))] (when (gobj/get props "show") (mf/element dropdown-menu' diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index 75f6ee25a..15361c42f 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -16,10 +16,10 @@ [app.util.forms :as fm] [app.util.i18n :as i18n :refer [tr]] [app.util.keyboard :as kbd] - [app.util.object :as obj] [cljs.core :as c] [cuerdas.core :as str] - [rumext.v2 :as mf])) + [rumext.v2 :as mf] + [rumext.v2.util :as mfu])) (def form-ctx (mf/create-context nil)) (def use-form fm/use-form) @@ -102,7 +102,7 @@ (cond-> (and value is-checkbox?) (assoc :default-checked value)) (cond-> (and touched? (:message error)) (assoc "aria-invalid" "true" "aria-describedby" (dm/str "error-" input-name))) - (obj/map->obj obj/prop-key-fn)) + (mfu/map->props)) checked? (and is-checkbox? (= value true)) show-valid? (and show-success? touched? (not error)) @@ -205,7 +205,7 @@ :on-blur on-blur ;; :placeholder label :on-change on-change) - (obj/map->obj obj/prop-key-fn))] + (mfu/map->props))] [:div {:class (dm/str klass " " (stl/css :textarea-wrapper))} [:label {:class (stl/css :textarea-label)} label] diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 15f99ddf4..dfb609484 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -11,6 +11,7 @@ [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] + [app.common.json :as json] [app.common.svg :as csvg] [app.common.types.shape :refer [stroke-caps-line stroke-caps-marker]] [app.common.types.shape.radius :as ctsr] @@ -154,6 +155,7 @@ [shape render-id] (let [attrs (get shape :svg-attrs {}) defs (get shape :svg-defs {})] + (if (and (empty? defs) (empty? attrs)) #js {} @@ -164,7 +166,7 @@ (dm/str render-id "-" id) id))) (dissoc :id) - (obj/map->obj))))) + (json/->js :key-fn name))))) (defn get-fill-style ([fill-data index render-id type] diff --git a/frontend/src/app/main/ui/shapes/export.cljs b/frontend/src/app/main/ui/shapes/export.cljs index 0f216186f..339fa436d 100644 --- a/frontend/src/app/main/ui/shapes/export.cljs +++ b/frontend/src/app/main/ui/shapes/export.cljs @@ -12,9 +12,9 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] + [app.common.json :as json] [app.common.svg :as csvg] [app.main.ui.context :as muc] - [app.util.json :as json] [app.util.object :as obj] [cuerdas.core :as str] [rumext.v2 :as mf])) @@ -29,9 +29,11 @@ (cond (map? node) - [:> (d/name tag) (obj/map->obj (csvg/attrs->props attrs)) - (for [child content] - [:& render-xml {:xml child :key (swap! internal-counter inc)}])] + (let [props (-> (csvg/attrs->props attrs) + (json/->js :key-fn name))] + [:> (d/name tag) props + (for [child content] + [:& render-xml {:xml child :key (swap! internal-counter inc)}])]) (string? node) node @@ -39,14 +41,6 @@ :else nil)) -(defn uuid->string [m] - (->> m - (d/deep-mapm - (fn [[k v]] - (if (uuid? v) - [k (str v)] - [k v]))))) - (defn bool->str [val] (when (some? val) (str val))) @@ -130,8 +124,8 @@ (add! :width) (add! :height) (add! :grow-type) - (add! :content (comp json/encode uuid->string)) - (add! :position-data (comp json/encode uuid->string)))) + (add! :content json/encode) + (add! :position-data json/encode))) (cond-> mask? (obj/set! "penpot:masked-group" "true")) diff --git a/frontend/src/app/main/ui/shapes/svg_defs.cljs b/frontend/src/app/main/ui/shapes/svg_defs.cljs index f636bf205..39328b752 100644 --- a/frontend/src/app/main/ui/shapes/svg_defs.cljs +++ b/frontend/src/app/main/ui/shapes/svg_defs.cljs @@ -12,8 +12,8 @@ [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] [app.common.geom.shapes.bounds :as gsb] + [app.common.json :as json] [app.common.svg :as csvg] - [app.util.object :as obj] [rumext.v2 :as mf])) (defn add-matrix [attrs transform-key transform-matrix] @@ -79,12 +79,16 @@ :data-old-width (:width attrs) :data-old-height (:height attrs)})) - [wrapper wrapper-props] (if (= tag :mask) - ["g" #js {:className "svg-mask-wrapper" - :transform (str transform)}] - [mf/Fragment #js {}])] + [wrapper wrapper-props] + (if (= tag :mask) + ["g" #js {:className "svg-mask-wrapper" + :transform (str transform)}] + [mf/Fragment #js {}]) - [:> (name tag) (obj/map->obj attrs) + props + (json/->js attrs :key-fn name)] + + [:> (name tag) props [:> wrapper wrapper-props (for [[index node] (d/enumerate content)] [:& svg-node {:key (dm/str "node-" index) diff --git a/frontend/src/app/plugins/format.cljs b/frontend/src/app/plugins/format.cljs index 42d116b39..b734decad 100644 --- a/frontend/src/app/plugins/format.cljs +++ b/frontend/src/app/plugins/format.cljs @@ -35,7 +35,7 @@ (defn format-point [{:keys [x y] :as point}] (when (some? point) - (obj/clear-empty + (obj/without-empty #js {:x x :y y}))) ;;export type PenpotBounds = { @@ -47,7 +47,7 @@ (defn format-bounds [{:keys [x y width height] :as bounds}] (when (some? bounds) - (obj/clear-empty + (obj/without-empty #js {:x x :y y :width width :height height}))) ;; export interface PenpotColorShapeInfoEntry { @@ -58,7 +58,7 @@ (defn format-shape-info [{:keys [prop shape-id index] :as info}] (when (some? info) - (obj/clear-empty + (obj/without-empty #js {:property (d/name prop) :index index :shapeId (dm/str shape-id)}))) @@ -75,12 +75,12 @@ (defn format-stop [{:keys [color opacity offset] :as stop}] (when (some? stop) - (obj/clear-empty #js {:color color :opacity opacity :offset offset}))) + (obj/without-empty #js {:color color :opacity opacity :offset offset}))) (defn format-gradient [{:keys [type start-x start-y end-x end-y width stops] :as gradient}] (when (some? gradient) - (obj/clear-empty + (obj/without-empty #js {:type (format-key type) :startX start-x :startY start-y @@ -100,7 +100,7 @@ (defn format-image [{:keys [name width height mtype id keep-aspect-ratio] :as image}] (when (some? image) - (obj/clear-empty + (obj/without-empty #js {:name name :width width :height height @@ -122,7 +122,7 @@ (defn format-color [{:keys [id name path color opacity ref-id ref-file gradient image] :as color-data}] (when (some? color-data) - (obj/clear-empty + (obj/without-empty #js {:id (format-id id) :name name :path path @@ -155,7 +155,7 @@ (defn format-shadow [{:keys [id style offset-x offset-y blur spread hidden color] :as shadow}] (when (some? shadow) - (obj/clear-empty + (obj/without-empty #js {:id (-> id format-id) :style (-> style format-key) :offsetX offset-x @@ -181,7 +181,7 @@ (defn format-fill [{:keys [fill-color fill-opacity fill-color-gradient fill-color-ref-file fill-color-ref-id fill-image] :as fill}] (when (some? fill) - (obj/clear-empty + (obj/without-empty #js {:fillColor fill-color :fillOpacity fill-opacity :fillColorGradient (format-gradient fill-color-gradient) @@ -219,7 +219,7 @@ stroke-cap-start stroke-cap-end stroke-color-gradient] :as stroke}] (when (some? stroke) - (obj/clear-empty + (obj/without-empty #js {:strokeColor stroke-color :strokeColorRefFile (format-id stroke-color-ref-file) :strokeColorRefId (format-id stroke-color-ref-id) @@ -245,7 +245,7 @@ (defn format-blur [{:keys [id type value hidden] :as blur}] (when (some? blur) - (obj/clear-empty + (obj/without-empty #js {:id (format-id id) :type (format-key type) :value value @@ -259,7 +259,7 @@ (defn format-export [{:keys [type scale suffix] :as export}] (when (some? export) - (obj/clear-empty + (obj/without-empty #js {:type (format-key type) :scale scale :suffix suffix}))) @@ -280,7 +280,7 @@ (defn format-frame-guide-column-params [{:keys [color type size margin item-length gutter] :as params}] (when (some? params) - (obj/clear-empty + (obj/without-empty #js {:color (format-color color) :type (format-key type) :size size @@ -296,7 +296,7 @@ (defn format-frame-guide-column [{:keys [type display params] :as guide}] (when (some? guide) - (obj/clear-empty + (obj/without-empty #js {:type (format-key type) :display display :params (format-frame-guide-column-params params)}))) @@ -309,7 +309,7 @@ (defn format-frame-guide-row [{:keys [type display params] :as guide}] (when (some? guide) - (obj/clear-empty + (obj/without-empty #js {:type (format-key type) :display display :params (format-frame-guide-column-params params)}))) @@ -321,7 +321,7 @@ (defn format-frame-guide-square-params [{:keys [color size] :as params}] (when (some? params) - (obj/clear-empty + (obj/without-empty #js {:color (format-color color) :size size}))) @@ -334,7 +334,7 @@ (defn format-frame-guide-square [{:keys [type display params] :as guide}] (when (some? guide) - (obj/clear-empty + (obj/without-empty #js {:type (format-key type) :display display :params (format-frame-guide-column-params params)}))) @@ -382,7 +382,7 @@ (defn format-command-params [{:keys [x y c1x c1y c2x c2y rx ry x-axis-rotation large-arc-flag sweep-flag] :as props}] (when (some? props) - (obj/clear-empty + (obj/without-empty #js {:x x :y y :c1x c1x @@ -398,7 +398,7 @@ (defn format-command [{:keys [command params] :as props}] (when (some? props) - (obj/clear-empty + (obj/without-empty #js {:command (format-key command) :params (format-command-params params)}))) @@ -416,7 +416,7 @@ (defn format-track [{:keys [type value] :as track}] (when (some? track) - (obj/clear-empty + (obj/without-empty #js {:type (-> type format-key) :value value}))) @@ -462,7 +462,7 @@ (defn format-animation [animation] (when animation - (obj/clear-empty + (obj/without-empty (case (:animation-type animation) :dissolve @@ -543,7 +543,7 @@ (defn format-action [interaction plugin file-id page-id] (when interaction - (obj/clear-empty + (obj/without-empty (case (:action-type interaction) :navigate #js {:type "navigate-to" diff --git a/frontend/src/app/util/object.cljs b/frontend/src/app/util/object.cljs index 707bef217..6066eb952 100644 --- a/frontend/src/app/util/object.cljs +++ b/frontend/src/app/util/object.cljs @@ -6,12 +6,7 @@ (ns app.util.object "A collection of helpers for work with javascript objects." - (:refer-clojure :exclude [set! new get get-in merge clone contains? array? into-array]) - (:require - [cuerdas.core :as str] - ;; FIXME: we use goog.string here for performance reasons, pending - ;; to apply this optimizations directly to cuerdas. - [goog.string :as gstr])) + (:refer-clojure :exclude [set! new get merge clone contains? array? into-array])) (defn array? [o] @@ -36,24 +31,6 @@ (when (some? obj) (js/Object.hasOwn obj k))) -(defn get-keys - [obj] - (js/Object.keys ^js obj)) - -(defn get-in - ([obj keys] - (get-in obj keys nil)) - - ([obj keys default] - (loop [key (first keys) - keys (rest keys) - res obj] - (if (or (nil? key) (nil? res)) - (or res default) - (recur (first keys) - (rest keys) - (unchecked-get res key)))))) - (defn clone [a] (js/Object.assign #js {} a)) @@ -80,86 +57,20 @@ (js-delete obj key) obj) +(def ^:private not-found-sym (js/Symbol "not-found")) + (defn update! [obj key f & args] - (let [found (get obj key ::not-found)] - (if-not (identical? ::not-found found) - (do (unchecked-set obj key (apply f found args)) - obj) - obj))) - -(defn- props-key-fn - [k] - (if (or (keyword? k) (symbol? k)) - (let [nword (name k)] - (cond - (= nword "class") "className" - (str/starts-with? nword "--") nword - (str/starts-with? nword "data-") nword - (str/starts-with? nword "aria-") nword - :else (str/camel nword))) - k)) - -(defn clj->props - [props] - (clj->js props :keyword-fn props-key-fn)) + (let [found (get obj key not-found-sym)] + (when-not ^boolean (identical? found not-found-sym) + (unchecked-set obj key (apply f found args))) + obj)) (defn ^boolean in? [obj prop] (js* "~{} in ~{}" prop obj)) -(defn- transform-prop-key - [s] - (let [result (js* "~{}.replace(\":\", \"-\").replace(/-./g, x=>x[1].toUpperCase())", s)] - (if ^boolean (gstr/startsWith s "-") - (gstr/capitalize result) - result))) - -(defn prop-key-fn - [k] - (when (string? k) - (cond - (or (= k "class") - (= k "class-name")) - "className" - - (gstr/startsWith k "data-") - k - - :else - (transform-prop-key k)))) - - -;; FIXME: REPEATED from app.common.json -(defn map->obj - "A simplified version of clj->js with focus on performance" - ([x] (map->obj x identity)) - ([x ^function key-fn] - (cond - (nil? x) - nil - - (keyword? x) - (name x) - - (map? x) - (reduce-kv (fn [m k v] - (let [k (if (keyword? k) (name k) k)] - (unchecked-set m (key-fn k) (map->obj v key-fn)) - m)) - #js {} - x) - - (coll? x) - (reduce (fn [arr v] - (.push arr v) - arr) - (array) - x) - - :else x))) - -(defn clear-empty +(defn without-empty [^js obj] (when (some? obj) (js* "Object.entries(~{}).reduce((a, [k,v]) => (v == null ? a : (a[k]=v, a)), {}) " obj)))