Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add iframe render mode #16

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/portfolio/react_utils.cljs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns portfolio.react-utils
(:require [goog]
[goog.object :as o]
["react" :as react]
[portfolio.ui.actions :as actions]
[portfolio.adapter :as adapter]))
Expand All @@ -12,7 +13,7 @@
(adapter/prepare-scene impl)))

(defn get-scene [this]
(.. this -props -scene))
(o/getValueByKeys this "props" "scene"))

(defn create-safe-wrapper []
(let [ctor (fn [])]
Expand All @@ -32,7 +33,7 @@
(render [this]
(.createElement
react "div" #js {}
(if (some-> this .-state .-error)
(if (o/getValueByKeys this "state" "error")
""
(:component (get-scene this))))))
ctor))
66 changes: 33 additions & 33 deletions src/portfolio/ui.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -70,42 +70,42 @@
(defn start! [& [{:keys [on-render config canvas-tools extra-canvas-tools index get-indexable-data] :as opt}]]
(let [->diffable (partial search/get-diffables (or get-indexable-data search/get-indexable-data))]
(swap! app merge (create-app config canvas-tools extra-canvas-tools) {:index index})

(when-not (client/started? app)
(add-watch data/scenes ::app
(fn [_ _ old-scenes scenes]
(let [collections (get-collections scenes (:collections @app))
old-collections (get-collections old-scenes (:collections @app))]
(swap! app (fn [state]
(-> state
(assoc :scenes scenes)
(assoc :collections collections))))
(when (:reindex? opt true)
(index-content
app
{:ids (concat
(search/get-diff-keys (->diffable scenes) (->diffable old-scenes))
(search/get-diff-keys (->diffable collections) (->diffable old-collections)))})))
(eventually-execute app [:go-to-current-location])))
(fn [_ _ old-scenes scenes]
(let [collections (get-collections scenes (:collections @app))
old-collections (get-collections old-scenes (:collections @app))]
(swap! app (fn [state]
(-> state
(assoc :scenes scenes)
(assoc :collections collections))))
(when (:reindex? opt true)
(index-content
app
{:ids (concat
(search/get-diff-keys (->diffable scenes) (->diffable old-scenes))
(search/get-diff-keys (->diffable collections) (->diffable old-collections)))})))
(eventually-execute app [:go-to-current-location])))

(add-watch data/collections ::app
(fn [_ _ _ collections]
(let [old-collections (:collections @app)
collections (get-collections (:scenes @app) collections)]
(swap! app assoc :collections collections)
(when (:reindex? opt true)
(index-content app {:ids (search/get-diff-keys (->diffable collections) (->diffable old-collections))})))))

(add-tap render-scene)

(js/window.addEventListener
"message"
(fn [e]
(when (.. e -data -action)
(when-let [action (actions/get-action (.-data e))]
(actions/execute-action! app action)))))))
(fn [_ _ _ collections]
(let [old-collections (:collections @app)
collections (get-collections (:scenes @app) collections)]
(swap! app assoc :collections collections)
(when (:reindex? opt true)
(index-content app {:ids (search/get-diff-keys (->diffable collections) (->diffable old-collections))}))))))

(when-not (client/started? app)
(index-content app))
(if (.get (new js/URLSearchParams js/window.location.search) "portfolio.embed")
(client/start-embed-app app)
(do
(when-not (client/started? app)
(add-tap render-scene)
(js/window.addEventListener
"message"
(fn [e]
(when (.. e -data -action)
(when-let [action (actions/get-action (.-data e))]
(actions/execute-action! app action)))))
(index-content app))

(client/start-app app {:on-render on-render}))
(client/start-app app {:on-render on-render})))))
45 changes: 44 additions & 1 deletion src/portfolio/ui/client.cljs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
(ns portfolio.ui.client
"Bootstrap and render a Portfolio UI app instance"
(:require [dumdom.core :as d]
(:require [clojure.edn :as edn]
[dumdom.core :as d]
[goog.object :as o]
[portfolio.adapter :as adapter]
[portfolio.homeless :as h]
[portfolio.ui.actions :as actions]
[portfolio.ui.collection :as collection]
Expand Down Expand Up @@ -108,3 +111,43 @@
[:go-to-current-location])))
(swap! app assoc ::started? true)))))
app)

(defn mark-embed-ready!
[app]
(set! (.-portfolioReady js/window) true)
(swap! app assoc ::started? true)
(js/window.parent.postMessage #js {:portfolio_ready true} "*"))

(defn track-host-location
[app]
(js/window.addEventListener
"message"
(fn [e]
(let [message (.-data e)]
(when-let [scene-id (o/get message "set_scene")]
(swap! app assoc ::opt (some-> message (o/get "opt") edn/read-string))
(actions/execute-action! app [:go-to-location {:query-params {:id scene-id
:portfolio.embed true}}]))))))

(defn render-component [app {:keys [on-render]}]
(let [state @app]
(when (ifn? on-render)
(on-render
;; TODO: add page-data parameter
#_page-data))
(if-let [el (js/document.getElementById "portfolio")]
(when-let [{:keys [target]} (collection/get-selection state (routes/get-id (:location state)))]
(adapter/render-component (assoc target :component ((:component-fn target) nil (::opt state))) el)
(js/window.parent.postMessage #js {:portfolio_render (routes/get-id (:location state))}))
(js/console.error "Unable to render portfolio: no element with id \"portfolio\""))))

(defn start-embed-app [app & [{:keys [on-render]}]]
(css/load-css-files-direct (:css-paths @app))
(if (started? app)
(render-component app {:on-render on-render})
(do
(ensure-element!)
(track-host-location app)
(add-watch app ::render (fn [_ _ _ _] (render-component app {:on-render on-render})))
(mark-embed-ready! app)))
app)
111 changes: 86 additions & 25 deletions src/portfolio/ui/components/canvas.cljs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
(ns portfolio.ui.components.canvas
(:require [clojure.string :as str]
[dumdom.core :as d]
[goog.object :as o]
[portfolio.adapter :as adapter]
[portfolio.ui.actions :as actions]
[portfolio.ui.canvas.protocols :as canvas]
Expand Down Expand Up @@ -33,7 +34,23 @@
(defn render-scene [el {:keys [scene tools opt]}]
(let [iframe (get-iframe el)
canvas (some-> iframe .-contentDocument (.getElementById "canvas"))
error (.querySelector el ".error-container")]
error (.querySelector el ".error-container")
scene-id-str (->> [(namespace (:id scene))
(name (:id scene))]
(remove empty?)
(str/join "/"))
finalize-fn (fn []
(doseq [tool tools]
(try
(canvas/finalize-canvas tool el opt)
(catch :default e
(-> (str "Failed to finalize canvas with " (:id tool))
(report-error e scene)))))
(when-let [win (.-contentWindow iframe)]
(.postMessage
win
(clj->js {:event "scene-rendered"
:scene-id scene-id-str}) "*")))]
(when error
(.removeChild (.-parentNode error) error)
(set! (.. iframe -style -display) "block"))
Expand All @@ -44,24 +61,32 @@
(-> (str "Failed to prepare canvas with " (:id tool))
(report-error e scene)))))
(try
(adapter/render-component (assoc scene :component ((:component-fn scene))) canvas)
(js/setTimeout
(fn []
(doseq [tool tools]
(try
(canvas/finalize-canvas tool el opt)
(catch :default e
(-> (str "Failed to finalize canvas with " (:id tool))
(report-error e scene)))))
(when-let [win (.-contentWindow iframe)]
(.postMessage
win
(clj->js {:event "scene-rendered"
:scene-id (->> [(namespace (:id scene))
(name (:id scene))]
(remove empty?)
(str/join "/"))}) "*")))
50)
(case (:render-mode scene :mount)
:mount
(do
(adapter/render-component (assoc scene :component ((:component-fn scene))) canvas)
(js/setTimeout finalize-fn 50))

:iframe
(let [set-scene! #(some-> iframe .-contentWindow
(.postMessage #js {:set_scene scene-id-str
:opt (pr-str opt)}))]
(js/window.addEventListener
"message"
(fn finalize [e]
(when (and (identical? (.-contentWindow iframe) (.-source e))
(o/getValueByKeys e "data" "portfolio_render"))
(js/window.removeEventListener "message" finalize)
(js/setTimeout finalize-fn 50))))
(if (-> iframe .-contentWindow (o/get "portfolioReady"))
(set-scene!)
(js/window.addEventListener
"message"
(fn set-the-scene [e]
(when (and (identical? (.-contentWindow iframe) (.-source e))
(o/getValueByKeys e "data" "portfolio_ready"))
(js/window.removeEventListener "message" set-the-scene)
(set-scene!)))))))
(catch :default e
(-> (str "Failed to render " (str "'" (:title scene) "'"))
(report-error e scene))))))
Expand All @@ -80,6 +105,13 @@
(.appendChild (.-body doc) el)))
(f))))))

(defn- pad-canvas [data document]
(let [[t r b l] (:viewport/padding (:opt data))]
(when t (set! (.. document -body -style -paddingTop) (str t "px")))
(when r (set! (.. document -body -style -paddingBottom) (str r "px")))
(when b (set! (.. document -documentElement -style -paddingLeft) (str b "px")))
(when l (set! (.. document -documentElement -style -paddingRight) (str l "px")))))

(defn init-canvas [el data f]
(let [iframe (get-iframe el)
document (get-iframe-document el)
Expand Down Expand Up @@ -122,11 +154,7 @@
(.appendChild head link)))

;; Set padding properties
(let [[t r b l] (:viewport/padding (:opt data))]
(when t (set! (.. document -body -style -paddingTop) (str t "px")))
(when r (set! (.. document -body -style -paddingBottom) (str r "px")))
(when b (set! (.. document -documentElement -style -paddingLeft) (str b "px")))
(when l (set! (.. document -documentElement -style -paddingRight) (str l "px"))))))
(pad-canvas data document)))

(defn get-rendered-data [{:keys [scene opt]}]
{:rendered (:rendered-data scene)
Expand Down Expand Up @@ -181,6 +209,35 @@
:height (when (number? (:viewport/height (:opt data)))
(:viewport/height (:opt data)))}}]])

(d/defcomponent IframeCanvas
:on-mount (fn [el data]
(enqueue-render-data el data)
(on-mounted
(get-iframe el)
(fn []
(pad-canvas data (get-iframe-document el))
(set! (.-renderFromQueue el) true)
(process-render-queue el))))
:on-update (fn [el data]
(enqueue-render-data el data))
[data]
[:div {:style {:background (or (:background/background-color (:opt data))
"var(--canvas-bg)")
:display "flex"
:transition "width 0.25s, height 0.25s"}}
[:iframe.canvas
{:src
(str (doto (new js/URL js/window.location)
(set! -search (new js/URLSearchParams #js {"portfolio.embed" true}))))
:title "Component scene"
:style {:border "none"
:flex-grow "1"
:width (or (when (number? (:viewport/width (:opt data)))
(:viewport/width (:opt data)))
"100%")
:height (when (number? (:viewport/height (:opt data)))
(:viewport/height (:opt data)))}}]])

(d/defcomponent Toolbar [{:keys [buttons]}]
[:nav {:style {:background "var(--bg)"
:color "var(--fg)"
Expand Down Expand Up @@ -242,7 +299,11 @@
(if (or (not (:component-fn (:scene data)))
(:error (:scene data)))
(Error (:error (:scene data)))
(Canvas data)))]
(case (:render-mode (:scene data) :mount)
:mount
(Canvas data)
:iframe
(IframeCanvas data))))]
(remove nil?))])

(def direction
Expand Down
5 changes: 5 additions & 0 deletions src/portfolio/ui/css.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
(when-not (find-link-by-href js/document.head path)
(.appendChild js/document.head (create-css-link path {:media "portfolio"})))))

(defn load-css-files-direct [paths]
(doseq [path paths]
(when-not (find-link-by-href js/document.head path)
(.appendChild js/document.head (create-css-link path)))))

(defn on-head-mutation [mutations paths]
(let [paths (set paths)]
(doseq [path (->> mutations
Expand Down