mirror of
https://github.com/penpot/penpot.git
synced 2025-06-18 12:51:38 +02:00
✨ Dynamic preview html output
This commit is contained in:
parent
723c14bef2
commit
641f8fb250
9 changed files with 217 additions and 20 deletions
110
frontend/src/app/main/data/preview.cljs
Normal file
110
frontend/src/app/main/data/preview.cljs
Normal file
|
@ -0,0 +1,110 @@
|
|||
;; 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.data.preview
|
||||
(:require
|
||||
["js-beautify" :as beautify]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.types.shape-tree :as ctst]
|
||||
[app.main.data.workspace.state-helpers :as wsh]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.util.code-gen :as cg]
|
||||
[app.util.timers :as ts]
|
||||
[beicon.core :as rx]
|
||||
[clojure.set :as set]
|
||||
[cuerdas.core :as str]
|
||||
[potok.core :as ptk]))
|
||||
|
||||
(def style-type "css")
|
||||
(def markup-type "html")
|
||||
|
||||
|
||||
(def page-template
|
||||
"<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
%s
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
%s
|
||||
</body>
|
||||
</html>")
|
||||
|
||||
(defn format-code [code type]
|
||||
(cond-> code
|
||||
(= type "svg")
|
||||
(-> (str/replace "<defs></defs>" "")
|
||||
(str/replace "><" ">\n<"))
|
||||
|
||||
(or (= type "svg") (= type "html"))
|
||||
(beautify/html #js {"indent_size" 2})))
|
||||
|
||||
(defn update-preview-window
|
||||
[preview code]
|
||||
(when preview
|
||||
(if (aget preview "load")
|
||||
(.load preview code)
|
||||
(ts/schedule #(update-preview-window preview code)))))
|
||||
|
||||
(defn shapes->fonts
|
||||
[shapes]
|
||||
(->> shapes
|
||||
(filter cph/text-shape?)
|
||||
(map (comp fonts/get-content-fonts :content))
|
||||
(reduce set/union #{})))
|
||||
|
||||
(defn update-preview
|
||||
[preview shape-id]
|
||||
(ptk/reify ::update-preview
|
||||
ptk/EffectEvent
|
||||
(effect [_ state _]
|
||||
(let [objects (wsh/lookup-page-objects state)
|
||||
shape (get objects shape-id)
|
||||
|
||||
all-children
|
||||
(->> (cph/selected-with-children objects [shape-id])
|
||||
(ctst/sort-z-index objects)
|
||||
(keep (d/getf objects)))
|
||||
|
||||
fonts (shapes->fonts all-children)]
|
||||
|
||||
(->> (rx/from fonts)
|
||||
(rx/merge-map fonts/fetch-font-css)
|
||||
(rx/reduce conj [])
|
||||
(rx/map #(str/join "\n" %))
|
||||
(rx/subs
|
||||
(fn [fontfaces-css]
|
||||
(let [style-code
|
||||
(dm/str
|
||||
fontfaces-css "\n"
|
||||
(-> (cg/generate-style-code objects style-type all-children)
|
||||
(format-code style-type)))
|
||||
|
||||
markup-code
|
||||
(-> (cg/generate-markup-code objects markup-type [shape])
|
||||
(format-code markup-type))]
|
||||
|
||||
(update-preview-window preview (str/format page-template style-code markup-code))))))))))
|
||||
|
||||
(defn open-preview-selected
|
||||
[]
|
||||
(ptk/reify ::open-preview-selected
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [shape-id (first (wsh/lookup-selected state))
|
||||
closed-preview (rx/subject)
|
||||
preview (.open js/window "/#/frame-preview")
|
||||
listener-fn #(rx/push! closed-preview true)]
|
||||
(.addEventListener preview "beforeunload" listener-fn)
|
||||
(->> (rx/from-atom (refs/all-children-objects shape-id) {:emit-current-value? true})
|
||||
(rx/take-until closed-preview)
|
||||
(rx/debounce 1000)
|
||||
(rx/map #(update-preview preview shape-id)))))))
|
|
@ -16,6 +16,7 @@
|
|||
[app.main.ui.cursors :as c]
|
||||
[app.main.ui.dashboard :refer [dashboard]]
|
||||
[app.main.ui.debug.components-preview :as cm]
|
||||
[app.main.ui.frame-preview :as frame-preview]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.messages :as msgs]
|
||||
[app.main.ui.onboarding]
|
||||
|
@ -135,6 +136,9 @@
|
|||
:page-id page-id
|
||||
:layout-name layout
|
||||
:key file-id}])
|
||||
|
||||
:frame-preview
|
||||
[:& frame-preview/frame-preview]
|
||||
nil)]]))
|
||||
|
||||
(mf/defc app
|
||||
|
|
73
frontend/src/app/main/ui/frame_preview.cljs
Normal file
73
frontend/src/app/main/ui/frame_preview.cljs
Normal file
|
@ -0,0 +1,73 @@
|
|||
;; 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.frame-preview
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[rumext.v2 :as mf]))
|
||||
|
||||
(mf/defc frame-preview
|
||||
{::mf/wrap-props false
|
||||
::mf/wrap [mf/memo]}
|
||||
[]
|
||||
|
||||
(let [iframe-ref (mf/use-ref nil)
|
||||
last-data* (mf/use-state nil)
|
||||
|
||||
zoom-ref (mf/use-ref nil)
|
||||
zoom* (mf/use-state 1)
|
||||
zoom @zoom*
|
||||
|
||||
|
||||
handle-load
|
||||
(mf/use-callback
|
||||
(fn [data]
|
||||
(prn "handle-load" data)
|
||||
(reset! last-data* data)
|
||||
(let [iframe-dom (mf/ref-val iframe-ref)]
|
||||
(when iframe-dom
|
||||
(-> iframe-dom .-contentWindow .-document .open)
|
||||
(-> iframe-dom .-contentWindow .-document (.write data))
|
||||
(-> iframe-dom .-contentWindow .-document .close)))))
|
||||
|
||||
load-ref
|
||||
(mf/use-callback
|
||||
(fn [iframe-dom]
|
||||
(.log js/console "load-ref" iframe-dom)
|
||||
(mf/set-ref-val! iframe-ref iframe-dom)
|
||||
(when (and iframe-dom @last-data*)
|
||||
(-> iframe-dom .-contentWindow .-document .open)
|
||||
(-> iframe-dom .-contentWindow .-document (.write @last-data*))
|
||||
(-> iframe-dom .-contentWindow .-document .close))))
|
||||
|
||||
change-zoom
|
||||
(mf/use-callback
|
||||
(fn []
|
||||
(let [zoom-level (d/parse-integer (.-value (mf/ref-val zoom-ref)))]
|
||||
(reset! zoom* (/ zoom-level 100)))))]
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(aset js/window "load" handle-load)
|
||||
#(js-delete js/window "load")))
|
||||
|
||||
[:div {:style {:display "flex" :width "100%" :height "100%" :flex-direction "column" :overflow "auto" :align-items "center"}}
|
||||
[:input {:id "zoom-input"
|
||||
:ref zoom-ref
|
||||
:type "range" :min 1 :max 200 :default-value 100
|
||||
:on-change change-zoom
|
||||
:style {:max-width "500px"}}]
|
||||
|
||||
[:div {:style {:width "100%" :height "100%" :overflow "auto"}}
|
||||
[:iframe {:ref load-ref
|
||||
:frameborder "0"
|
||||
:scrolling "no"
|
||||
:style {:width (str (* 100 (if (> zoom 1)
|
||||
(* 1 zoom)
|
||||
(/ 1 zoom))) "%")
|
||||
:height "100%"
|
||||
:transform-origin "left top"
|
||||
:transform (str "scale(" zoom ")")}}]]]))
|
|
@ -50,6 +50,7 @@
|
|||
["/options" :settings-options]
|
||||
["/access-tokens" :settings-access-tokens]]
|
||||
|
||||
["/frame-preview" :frame-preview]
|
||||
["/view/:file-id"
|
||||
{:name :viewer
|
||||
:conform
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.common.types.shape-tree :as ctst]
|
||||
[app.config :as cfg]
|
||||
[app.main.data.events :as ev]
|
||||
;; [app.main.data.preview :as dp]
|
||||
[app.main.fonts :as fonts]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
|
@ -95,6 +96,16 @@
|
|||
(str/replace value old new))
|
||||
value map))
|
||||
|
||||
(defn gen-all-code
|
||||
[style-code markup-code images-data]
|
||||
(let [markup-code (cond-> markup-code
|
||||
embed-images? (replace-map images-data))
|
||||
|
||||
style-code (cond-> style-code
|
||||
remove-localhost?
|
||||
(str/replace "http://localhost:3449" ""))]
|
||||
(str/format page-template style-code markup-code)))
|
||||
|
||||
(mf/defc code
|
||||
[{:keys [shapes frame on-expand from]}]
|
||||
(let [style-type* (mf/use-state "css")
|
||||
|
@ -110,16 +121,8 @@
|
|||
shapes (->> shapes
|
||||
(map #(gsh/translate-to-frame % frame)))
|
||||
|
||||
route (mf/deref refs/route)
|
||||
page-id (:page-id (:query-params route))
|
||||
flex-items (get-flex-elements page-id shapes from)
|
||||
objects (get-objects from)
|
||||
|
||||
;; TODO REMOVE THIS
|
||||
shapes (->> shapes
|
||||
(map #(assoc % :parent (get objects (:parent-id %))))
|
||||
(map #(assoc % :flex-items flex-items)))
|
||||
|
||||
all-children (->> shapes
|
||||
(map :id)
|
||||
(cph/selected-with-children objects)
|
||||
|
@ -194,15 +197,13 @@
|
|||
(mf/use-callback
|
||||
(mf/deps style-code markup-code images-data)
|
||||
(fn []
|
||||
(let [markup-code (cond-> markup-code
|
||||
embed-images? (replace-map images-data))
|
||||
(wapi/write-to-clipboard (gen-all-code style-code markup-code images-data))))
|
||||
|
||||
style-code (cond-> style-code
|
||||
remove-localhost?
|
||||
(str/replace "http://localhost:3449" ""))
|
||||
|
||||
data (str/format page-template style-code markup-code)]
|
||||
(wapi/write-to-clipboard data))))]
|
||||
;;handle-open-review
|
||||
;;(mf/use-callback
|
||||
;; (fn []
|
||||
;; (st/emit! (dp/open-preview-selected))))
|
||||
]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps fonts)
|
||||
|
@ -231,6 +232,10 @@
|
|||
[:button.download-button {:on-click handle-copy-all-code}
|
||||
"Copy all code"]]
|
||||
|
||||
#_[:div.attributes-block
|
||||
[:button.download-button {:on-click handle-open-review}
|
||||
"Preview"]]
|
||||
|
||||
[:div.code-block
|
||||
[:div.code-row-lang
|
||||
[:& select {:default-value style-type
|
||||
|
|
|
@ -21,7 +21,6 @@
|
|||
;;
|
||||
(def prelude "
|
||||
html, body {
|
||||
background-color: #E8E9EA;
|
||||
margin: 0;
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
(:layout-item-h-sizing shape)
|
||||
(:layout-item-v-sizing shape))]
|
||||
(cond
|
||||
(or (and (ctl/any-layout? shape) (= sizing :auto))
|
||||
(or (and (ctl/any-layout? shape) (= sizing :auto) (not (svg-markup? shape)))
|
||||
(and (ctl/any-layout-immediate-child? objects shape) (= sizing :fill)))
|
||||
sizing
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
[app.common.uuid :as uuid]
|
||||
[app.config :as cf]
|
||||
[app.main.data.dashboard.shortcuts]
|
||||
[app.main.data.preview :as dp]
|
||||
[app.main.data.viewer.shortcuts]
|
||||
[app.main.data.workspace :as dw]
|
||||
[app.main.data.workspace.changes :as dwc]
|
||||
|
@ -212,6 +213,10 @@
|
|||
[]
|
||||
(dump-selected' @st/state))
|
||||
|
||||
(defn ^:export preview-selected
|
||||
[]
|
||||
(st/emit! (dp/open-preview-selected)))
|
||||
|
||||
(defn ^:export parent
|
||||
[]
|
||||
(let [state @st/state
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue