;; This Source Code Form is subject to the terms of the Mozilla Public ;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; file, You can obtain one at http://mozilla.org/MPL/2.0/. ;; ;; Copyright (c) KALEIDOS INC (ns app.main.ui.shapes.frame (:require [app.common.data.macros :as dm] [app.common.files.helpers :as cfh] [app.common.geom.rect :as grc] [app.common.geom.shapes :as gsh] [app.common.types.shape.layout :as ctl] [app.config :as cf] [app.main.ui.context :as muc] [app.main.ui.shapes.attrs :as attrs] [app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]] [app.util.debug :as dbg] [app.util.object :as obj] [rumext.v2 :as mf])) (defn- frame-clip-id [shape render-id] (dm/str "frame-clip-" (dm/get-prop shape :id) "-" render-id)) (defn- frame-clip-url [shape render-id] (dm/str "url(#" (frame-clip-id shape render-id) ")")) (mf/defc frame-clip-def {::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape")] (when (and ^boolean (cfh/frame-shape? shape) (not ^boolean (:show-content shape))) (let [render-id (unchecked-get props "render-id") x (dm/get-prop shape :x) y (dm/get-prop shape :y) w (dm/get-prop shape :width) h (dm/get-prop shape :height) t (gsh/transform-str shape) props (mf/with-memo [shape] (-> #js {} (attrs/add-border-props! shape) (obj/merge! #js {:x x :y y :width w :height h :transform t}))) path? (some? (.-d props))] [:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip frame-clip-def"} (if ^boolean path? [:> :path props] [:> :rect props])])))) ;; Wrapper around the frame that will handle things such as strokes and other properties ;; we wrap the proper frames and also the thumbnails (mf/defc frame-container {::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") children (unchecked-get props "children") render-id (mf/use-ctx muc/render-id) x (dm/get-prop shape :x) y (dm/get-prop shape :y) w (dm/get-prop shape :width) h (dm/get-prop shape :height) transform (gsh/transform-str shape) show-content? (get shape :show-content) props (mf/with-memo [shape] (-> #js {} (attrs/add-border-props! shape) (obj/merge! #js {:x x :y y :width w :height h :transform transform :className "frame-background"}))) path? (some? (.-d props))] [:* [:g {:clip-path (when-not ^boolean show-content? (frame-clip-url shape render-id)) ;; A frame sets back normal fill behavior (default ;; transparent). It may have been changed to default black ;; if a shape coming from an imported SVG file is ;; rendered. See main.ui.shapes.attrs/add-style-attrs. :fill "none"} [:& shape-fills {:shape shape} (if ^boolean path? [:> :path props] [:> :rect props])] children] [:& shape-strokes {:shape shape} (if ^boolean path? [:> :path props] [:> :rect props])]])) (mf/defc frame-thumbnail-image {::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") bounds (unchecked-get props "bounds") shape-id (dm/get-prop shape :id) points (dm/get-prop shape :points) bounds (mf/with-memo [bounds points] (or bounds (grc/points->rect points))) thumb (:thumbnail shape) debug? (dbg/enabled? :thumbnails) safari? (cf/check-browser? :safari) ;; FIXME: ensure bounds is always a rect instance and ;; dm/get-prop for static attr access bx (:x bounds) by (:y bounds) bh (:height bounds) bw (:width bounds)] [:* [:image.frame-thumbnail {:id (dm/str "thumbnail-" shape-id) :href thumb :x bx :y by :width bw :height bh :decoding "async" :style {:filter (when (and (not ^boolean safari?) ^boolean debug?) "sepia(1)")}}] ;; Safari don't support filters so instead we add a rectangle around the thumbnail (when (and ^boolean safari? ^boolean debug?) [:rect {:x (+ bx 4) :y (+ by 4) :width (- bw 8) :height (- bh 8) :stroke "red" :stroke-width 2}])])) (mf/defc frame-thumbnail {::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape")] (when ^boolean (:thumbnail shape) [:> frame-container props [:> frame-thumbnail-image props]]))) (defn frame-shape [shape-wrapper] (mf/fnc frame-shape {::mf/wrap-props false} [props] (let [shape (unchecked-get props "shape") childs (unchecked-get props "childs") childs (cond-> childs (ctl/any-layout? shape) (cfh/sort-layout-children-z-index))] [:> frame-container props [:g.frame-children {:opacity (:opacity shape)} (for [item childs] (let [id (dm/get-prop item :id)] (when (some? id) [:& shape-wrapper {:key (dm/str id) :shape item}])))]])))