mirror of
https://github.com/penpot/penpot.git
synced 2025-01-25 07:58:49 -05:00
Merge pull request #2443 from penpot/palba-figma-plugin
🎉 Basic changes to use penpot as a library
This commit is contained in:
commit
a59ca5b781
11 changed files with 96 additions and 43 deletions
|
@ -21,6 +21,7 @@
|
|||
[app.common.types.pages-list :as ctpl]
|
||||
[app.common.types.shape :as cts]
|
||||
[app.common.uuid :as uuid]
|
||||
[clojure.spec.alpha :as spec]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(def root-frame uuid/zero)
|
||||
|
@ -52,9 +53,18 @@
|
|||
(when fail-on-spec?
|
||||
(us/verify ::pcs/change change))
|
||||
|
||||
(let [valid? (us/valid? ::pcs/change change)]
|
||||
(let [valid? (us/valid? ::pcs/change change)
|
||||
explain (spec/explain-str ::pcs/change change)]
|
||||
#?(:cljs
|
||||
(when-not valid? (.warn js/console "Invalid shape" (clj->js change))))
|
||||
(when-not valid?
|
||||
(do
|
||||
(.warn js/console "Invalid shape" (clj->js change))
|
||||
(.warn js/console explain)))
|
||||
:clj
|
||||
(when-not valid?
|
||||
(do
|
||||
(prn "Invalid shape" change)
|
||||
(prn explain))))
|
||||
|
||||
(cond-> file
|
||||
valid?
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
funcool/tubax {:mvn/version "2021.05.20-0"}
|
||||
|
||||
funcool/rumext
|
||||
{:git/tag "v2.0"
|
||||
:git/sha "fc617a8"
|
||||
:git/url "https://github.com/funcool/rumext.git"}
|
||||
{:git/tag "v2.1"
|
||||
:git/sha "6343102"
|
||||
:git/url "https://github.com/funcool/rumext.git"
|
||||
}
|
||||
|
||||
instaparse/instaparse {:mvn/version "1.4.12"}
|
||||
garden/garden {:git/url "https://github.com/noprompt/garden"
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
:createFile app.libs.file-builder/create-file-export}}}
|
||||
|
||||
:compiler-options
|
||||
{:output-feature-set :es8
|
||||
{:output-feature-set :es2020
|
||||
:output-wrapper false
|
||||
:warnings {:fn-deprecated false}}
|
||||
|
||||
|
|
|
@ -8,6 +8,11 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.file-builder :as fb]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.zip :as uz]
|
||||
[app.worker.export :as e]
|
||||
[beicon.core :as rx]
|
||||
[cuerdas.core :as str]))
|
||||
|
||||
(defn parse-data [data]
|
||||
|
@ -22,6 +27,50 @@
|
|||
key (-> key d/name str/kebab keyword)]
|
||||
[key value])) $)))
|
||||
|
||||
|
||||
(defn export-file
|
||||
[file]
|
||||
(let [file (assoc file
|
||||
:name (:name file)
|
||||
:file-name (:name file)
|
||||
:is-shared false)
|
||||
|
||||
files-stream (->> (rx/of {(:id file) file})
|
||||
(rx/share))
|
||||
|
||||
manifest-stream
|
||||
(->> files-stream
|
||||
(rx/map #(e/create-manifest (uuid/next) (:id file) :all %))
|
||||
(rx/map (fn [a]
|
||||
(vector "manifest.json" a))))
|
||||
|
||||
render-stream
|
||||
(->> files-stream
|
||||
(rx/flat-map vals)
|
||||
(rx/flat-map e/process-pages)
|
||||
(rx/observe-on :async)
|
||||
(rx/flat-map e/get-page-data)
|
||||
(rx/share))
|
||||
pages-stream
|
||||
(->> render-stream
|
||||
(rx/map e/collect-page))]
|
||||
|
||||
(rx/merge
|
||||
(->> render-stream
|
||||
(rx/map #(hash-map
|
||||
:type :progress
|
||||
:file (:id file)
|
||||
:data (str "Render " (:file-name %) " - " (:name %)))))
|
||||
|
||||
(->> (rx/merge
|
||||
manifest-stream
|
||||
pages-stream)
|
||||
(rx/reduce conj [])
|
||||
(rx/with-latest-from files-stream)
|
||||
(rx/flat-map (fn [[data _]]
|
||||
(->> (uz/compress-files data)
|
||||
(rx/map #(vector file %)))))))))
|
||||
|
||||
(deftype File [^:mutable file]
|
||||
Object
|
||||
|
||||
|
@ -50,6 +99,13 @@
|
|||
(closeGroup [_]
|
||||
(set! file (fb/close-group file)))
|
||||
|
||||
(addBool [_ data]
|
||||
(set! file (fb/add-bool file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
||||
(closeBool [_]
|
||||
(set! file (fb/close-bool file)))
|
||||
|
||||
(createRect [_ data]
|
||||
(set! file (fb/create-rect file (parse-data data)))
|
||||
(str (:last-id file)))
|
||||
|
@ -78,7 +134,15 @@
|
|||
(set! file (fb/close-svg-raw file)))
|
||||
|
||||
(asMap [_]
|
||||
(clj->js file)))
|
||||
(clj->js file))
|
||||
|
||||
(export [_]
|
||||
(->> (export-file file)
|
||||
(rx/subs
|
||||
(fn [value]
|
||||
(when (not (contains? value :type))
|
||||
(let [[file export-blob] value]
|
||||
(dom/trigger-download (:name file) export-blob))))))))
|
||||
|
||||
(defn create-file-export [^string name]
|
||||
(File. (fb/create-file name)))
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
(ns app.main.broadcast
|
||||
"BroadcastChannel API."
|
||||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.transit :as t]
|
||||
[beicon.core :as rx]
|
||||
[potok.core :as ptk]))
|
||||
|
@ -18,9 +19,12 @@
|
|||
(def ^:const default-topic "penpot")
|
||||
|
||||
;; The main broadcast channel instance, used for emit data
|
||||
;; If used as a library may be we can't access js/BroadcastChannel.
|
||||
;; and even if it exists we can receive an exception like:
|
||||
;; Failed to construct 'BroadcastChannel': Can't create BroadcastChannel in an opaque origin
|
||||
(defonce default-channel
|
||||
(when (exists? js/BroadcastChannel)
|
||||
(js/BroadcastChannel. default-topic)))
|
||||
(ex/ignoring (js/BroadcastChannel. default-topic))))
|
||||
|
||||
(defonce stream
|
||||
(if (exists? js/BroadcastChannel)
|
||||
|
|
|
@ -7,9 +7,7 @@
|
|||
(ns app.main.ui.hooks
|
||||
"A collection of general purpose react hooks."
|
||||
(:require
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.broadcast :as mbc]
|
||||
[app.main.data.shortcuts :as dsc]
|
||||
[app.main.refs :as refs]
|
||||
|
@ -22,11 +20,6 @@
|
|||
[goog.functions :as f]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(defn use-id
|
||||
"Get a stable id value across rerenders."
|
||||
[]
|
||||
(mf/use-memo #(dm/str (uuid/next))))
|
||||
|
||||
(defn use-rxsub
|
||||
[ob]
|
||||
(let [[state reset-state!] (mf/useState #(if (satisfies? IDeref ob) @ob nil))]
|
||||
|
@ -253,24 +246,6 @@
|
|||
(mf/set-ref-val! ref val))
|
||||
(mf/ref-val ref)))
|
||||
|
||||
(defn- ssr?
|
||||
"Checks if the current environment is run under a SSR context"
|
||||
[]
|
||||
(try
|
||||
(not js/window)
|
||||
(catch :default _e
|
||||
;; When exception accessing window we're in ssr
|
||||
true)))
|
||||
|
||||
(defn use-effect-ssr
|
||||
"Use effect that handles SSR"
|
||||
[deps effect-fn]
|
||||
|
||||
(if (ssr?)
|
||||
(let [ret (effect-fn)]
|
||||
(when (fn? ret) (ret)))
|
||||
(mf/use-effect deps effect-fn)))
|
||||
|
||||
(defn with-focus-objects
|
||||
([objects]
|
||||
(let [focus (mf/deref refs/workspace-focus-selected)]
|
||||
|
@ -298,7 +273,7 @@
|
|||
localStorage. And it will keep watching events with type equals to
|
||||
`key` for new values."
|
||||
[key default]
|
||||
(let [id (use-id)
|
||||
(let [id (mf/use-id)
|
||||
state (mf/use-state (get @storage key default))
|
||||
stream (mf/with-memo [id]
|
||||
(->> mbc/stream
|
||||
|
@ -311,7 +286,6 @@
|
|||
(swap! storage assoc key @state))
|
||||
|
||||
(use-stream stream (partial reset! state))
|
||||
|
||||
state))
|
||||
|
||||
(defonce ^:private intersection-subject (rx/subject))
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
uri-data (mf/use-ref {})
|
||||
state (mf/use-state 0)]
|
||||
|
||||
(hooks/use-effect-ssr
|
||||
(mf/use-ssr-effect
|
||||
(mf/deps embed? urls)
|
||||
(fn []
|
||||
(let [;; When not active the embedding we return the URI
|
||||
|
|
|
@ -80,7 +80,7 @@
|
|||
(obj/set! "height" height))])
|
||||
|
||||
(when has-image?
|
||||
[:image {:href (get embed uri uri)
|
||||
[:image {:href (or (:data-uri shape) (get embed uri uri))
|
||||
:preserveAspectRatio "none"
|
||||
:width width
|
||||
:height height}])]])])))))
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.hooks :as h]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.export :as ed]
|
||||
[app.main.ui.shapes.fills :as fills]
|
||||
|
@ -56,7 +55,7 @@
|
|||
disable-shadows? (unchecked-get props "disable-shadows?")
|
||||
|
||||
type (:type shape)
|
||||
render-id (h/use-id)
|
||||
render-id (mf/use-id)
|
||||
filter-id (dm/str "filter_" render-id)
|
||||
styles (-> (obj/create)
|
||||
(obj/set! "pointerEvents" pointer-events)
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.shapes.embed :as embed]
|
||||
[app.util.object :as obj]
|
||||
[beicon.core :as rx]
|
||||
|
@ -32,7 +31,7 @@
|
|||
(let [fonts-css-ref (mf/use-ref "")
|
||||
redraw (mf/use-state 0)]
|
||||
|
||||
(hooks/use-effect-ssr
|
||||
(mf/use-ssr-effect
|
||||
(mf/deps fonts)
|
||||
(fn []
|
||||
(let [sub
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.util.storage
|
||||
(:require
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.transit :as t]
|
||||
[app.util.globals :as g]
|
||||
[app.util.timers :as tm]))
|
||||
|
@ -38,7 +39,8 @@
|
|||
{}
|
||||
(range len)))))
|
||||
|
||||
|
||||
(defonce storage (atom (load (unchecked-get g/global "localStorage"))))
|
||||
;; Using ex/ignoring because can receive a DOMException like this when importing the code as a library:
|
||||
;; Failed to read the 'localStorage' property from 'Window': Storage is disabled inside 'data:' URLs.
|
||||
(defonce storage (atom (load (ex/ignoring (unchecked-get g/global "localStorage")))))
|
||||
|
||||
(add-watch storage :persistence #(persist js/localStorage %3 %4))
|
||||
|
|
Loading…
Add table
Reference in a new issue