0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-09 08:20:45 -05:00

🐛 Safari compatibility fixes

This commit is contained in:
alonso.torres 2020-11-30 17:59:39 +01:00 committed by Andrey Antukh
parent 9260c59afb
commit 001f90a540
12 changed files with 164 additions and 75 deletions

View file

@ -224,3 +224,8 @@ input[type=number]::-webkit-inner-spin-button,
input[type=number] { input[type=number] {
-moz-appearance: textfield; -moz-appearance: textfield;
} }
[contenteditable] {
-webkit-user-select: text;
user-select: text;
}

View file

@ -1,5 +1,4 @@
.debug-preview { .debug-preview {
max-height: 100vh;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
overflow: scroll; overflow: scroll;

View file

@ -9,9 +9,41 @@
(ns app.config (ns app.config
(:require (:require
[clojure.spec.alpha :as s]
[app.common.spec :as us]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.dom :as dom]
[cuerdas.core :as str])) [cuerdas.core :as str]))
(s/def ::platform #{:windows :linux :macos :other})
(s/def ::browser #{:chrome :mozilla :safari :edge :other})
(defn parse-browser
[]
(let [user-agent (-> (dom/get-user-agent) str/lower)
check-chrome? (fn [] (str/includes? user-agent "chrom"))
check-firefox? (fn [] (str/includes? user-agent "firefox"))
check-edge? (fn [] (str/includes? user-agent "edg"))
check-safari? (fn [] (str/includes? user-agent "safari"))]
(cond
(check-edge?) :edge
(check-chrome?) :chrome
(check-firefox?) :firefox
(check-safari?) :safari
:else :other)))
(defn parse-platform
[]
(let [user-agent (-> (dom/get-user-agent) str/lower)
check-windows? (fn [] (str/includes? user-agent "windows"))
check-linux? (fn [] (str/includes? user-agent "linux"))
check-macos? (fn [] (str/includes? user-agent "mac os"))]
(cond
(check-windows?) :windows
(check-linux?) :linux
(check-macos?) :macos
:else :other)))
(this-as global (this-as global
(def default-language "en") (def default-language "en")
(def demo-warning (obj/get global "appDemoWarning" false)) (def demo-warning (obj/get global "appDemoWarning" false))
@ -22,7 +54,17 @@
(def public-uri (or (obj/get global "appPublicURI") (def public-uri (or (obj/get global "appPublicURI")
(.-origin ^js js/location))) (.-origin ^js js/location)))
(def media-uri (str public-uri "/media")) (def media-uri (str public-uri "/media"))
(def default-theme "default")) (def default-theme "default")
(def browser (parse-browser))
(def platform (parse-platform)))
(defn ^boolean check-browser? [candidate]
(us/verify ::browser candidate)
(= candidate browser))
(defn ^boolean check-platform? [candidate]
(us/verify ::platform candidate)
(= candidate platform))
(defn resolve-media-path (defn resolve-media-path
[path] [path]

View file

@ -1625,45 +1625,53 @@
(deselect-all true)) (deselect-all true))
(rx/empty)))))) (rx/empty))))))
(defn c-mod
"Adds the control/command modifier to a shortcuts depending on the
operating system for the user"
[shortcut]
(if (cfg/check-platform? :macos)
(str "command+" shortcut)
(str "ctrl+" shortcut)))
(def shortcuts (def shortcuts
{"ctrl+i" #(st/emit! (toggle-layout-flags :assets)) {(c-mod "i") #(st/emit! (toggle-layout-flags :assets))
"ctrl+l" #(st/emit! (toggle-layout-flags :sitemap :layers)) (c-mod "l") #(st/emit! (toggle-layout-flags :sitemap :layers))
"ctrl+shift+r" #(st/emit! (toggle-layout-flags :rules)) (c-mod "shift+r") #(st/emit! (toggle-layout-flags :rules))
"ctrl+a" #(st/emit! (select-all)) (c-mod "a") #(st/emit! (select-all))
"ctrl+p" #(st/emit! (toggle-layout-flags :colorpalette)) (c-mod "p") #(st/emit! (toggle-layout-flags :colorpalette))
"ctrl+'" #(st/emit! (toggle-layout-flags :display-grid)) (c-mod "'") #(st/emit! (toggle-layout-flags :display-grid))
"ctrl+shift+'" #(st/emit! (toggle-layout-flags :snap-grid)) (c-mod "shift+'") #(st/emit! (toggle-layout-flags :snap-grid))
"+" #(st/emit! (increase-zoom nil)) "+" #(st/emit! (increase-zoom nil))
"-" #(st/emit! (decrease-zoom nil)) "-" #(st/emit! (decrease-zoom nil))
"ctrl+g" #(st/emit! group-selected) (c-mod "g") #(st/emit! group-selected)
"shift+g" #(st/emit! ungroup-selected) "shift+g" #(st/emit! ungroup-selected)
"ctrl+m" #(st/emit! mask-group) (c-mod "m") #(st/emit! mask-group)
"shift+m" #(st/emit! unmask-group) "shift+m" #(st/emit! unmask-group)
"ctrl+k" #(st/emit! dwl/add-component) (c-mod "k") #(st/emit! dwl/add-component)
"shift+0" #(st/emit! reset-zoom) "shift+0" #(st/emit! reset-zoom)
"shift+1" #(st/emit! zoom-to-fit-all) "shift+1" #(st/emit! zoom-to-fit-all)
"shift+2" #(st/emit! zoom-to-selected-shape) "shift+2" #(st/emit! zoom-to-selected-shape)
"ctrl+d" #(st/emit! duplicate-selected) (c-mod "d") #(st/emit! duplicate-selected)
"ctrl+z" #(st/emit! dwc/undo) (c-mod "z") #(st/emit! dwc/undo)
"ctrl+shift+z" #(st/emit! dwc/redo) (c-mod "shift+z") #(st/emit! dwc/redo)
"ctrl+y" #(st/emit! dwc/redo) (c-mod "y") #(st/emit! dwc/redo)
"ctrl+q" #(st/emit! dwc/reinitialize-undo) (c-mod "q") #(st/emit! dwc/reinitialize-undo)
"a" #(st/emit! (dwd/select-for-drawing :frame)) "a" #(st/emit! (dwd/select-for-drawing :frame))
"r" #(st/emit! (dwd/select-for-drawing :rect)) "r" #(st/emit! (dwd/select-for-drawing :rect))
"e" #(st/emit! (dwd/select-for-drawing :circle)) "e" #(st/emit! (dwd/select-for-drawing :circle))
"t" #(st/emit! dwtxt/start-edit-if-selected "t" #(st/emit! dwtxt/start-edit-if-selected
(dwd/select-for-drawing :text)) (dwd/select-for-drawing :text))
"p" #(st/emit! (dwd/select-for-drawing :path)) "p" #(st/emit! (dwd/select-for-drawing :path))
"ctrl+c" #(st/emit! copy-selected) (c-mod "c") #(st/emit! copy-selected)
"ctrl+v" #(st/emit! paste) (c-mod "v") #(st/emit! paste)
"ctrl+x" #(st/emit! copy-selected delete-selected) (c-mod "x") #(st/emit! copy-selected delete-selected)
"escape" #(st/emit! (esc-pressed)) "escape" #(st/emit! (esc-pressed))
"del" #(st/emit! delete-selected) "del" #(st/emit! delete-selected)
"backspace" #(st/emit! delete-selected) "backspace" #(st/emit! delete-selected)
"ctrl+up" #(st/emit! (vertical-order-selected :up)) (c-mod "up") #(st/emit! (vertical-order-selected :up))
"ctrl+down" #(st/emit! (vertical-order-selected :down)) (c-mod "down") #(st/emit! (vertical-order-selected :down))
"ctrl+shift+up" #(st/emit! (vertical-order-selected :top)) (c-mod "shift+up") #(st/emit! (vertical-order-selected :top))
"ctrl+shift+down" #(st/emit! (vertical-order-selected :bottom)) (c-mod "shift+down") #(st/emit! (vertical-order-selected :bottom))
"shift+up" #(st/emit! (dwt/move-selected :up true)) "shift+up" #(st/emit! (dwt/move-selected :up true))
"shift+down" #(st/emit! (dwt/move-selected :down true)) "shift+down" #(st/emit! (dwt/move-selected :down true))
"shift+right" #(st/emit! (dwt/move-selected :right true)) "shift+right" #(st/emit! (dwt/move-selected :right true))

View file

@ -55,10 +55,14 @@
(defn encode-svg-cursor (defn encode-svg-cursor
[id rotation x y height] [id rotation x y height]
(let [svg-path (str cursor-folder "/" (name id) ".svg") (let [svg-path (str cursor-folder "/" (name id) ".svg")
data (-> svg-path io/resource slurp parse-svg uri/percent-encode) data (-> svg-path io/resource slurp parse-svg)
transform (if rotation (str " transform='rotate(" rotation ")'") "")] data (uri/percent-encode data)
data (if rotation
(str/fmt "%3Cg transform='rotate(%s 8,8)'%3E%s%3C/g%3E" rotation data)
data)]
(str "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='20px' " (str "url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' width='20px' "
"height='" height "px' " transform "%3E" data "%3C/svg%3E\") " x " " y ", auto"))) "height='" height "px' %3E" data "%3C/svg%3E\") " x " " y ", auto")))
(defmacro cursor-ref (defmacro cursor-ref
"Creates a static cursor given its name, rotation and x/y hotspot" "Creates a static cursor given its name, rotation and x/y hotspot"

View file

@ -26,7 +26,7 @@
[{:keys [node index shape] :as props}] [{:keys [node index shape] :as props}]
(let [embed-resources? (mf/use-ctx muc/embed-ctx) (let [embed-resources? (mf/use-ctx muc/embed-ctx)
{:keys [type text children]} node {:keys [type text children]} node
props #js {:shape shape}
render-node render-node
(fn [index node] (fn [index node]
(mf/element text-node {:index index (mf/element text-node {:index index
@ -35,29 +35,31 @@
:shape shape}))] :shape shape}))]
(if (string? text) (if (string? text)
(let [style (sts/generate-text-styles (clj->js node))] (let [style (sts/generate-text-styles (clj->js node) props)]
[:span.text-node {:style style} (if (= text "") "\u00A0" text)]) [:span {:style style
:className (when (:fill-color-gradient node) "gradient")}
(if (= text "") "\u00A0" text)])
(let [children (map-indexed render-node children)] (let [children (map-indexed render-node children)]
(case type (case type
"root" "root"
(let [style (sts/generate-root-styles (clj->js node) #js {:shape shape})] (let [style (sts/generate-root-styles (clj->js node) props)]
[:div.root.rich-text [:div.root.rich-text
{:key index {:key index
:style style :style style
:xmlns "http://www.w3.org/1999/xhtml"} :xmlns "http://www.w3.org/1999/xhtml"}
[:* [:*
[:style ".text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"] [:style ".gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
(when embed-resources? (when embed-resources?
[ste/embed-fontfaces-style {:node node}])] [ste/embed-fontfaces-style {:node node}])]
children]) children])
"paragraph-set" "paragraph-set"
(let [style (sts/generate-paragraph-set-styles (clj->js node))] (let [style (sts/generate-paragraph-set-styles (clj->js node) props)]
[:div.paragraph-set {:key index :style style} children]) [:div.paragraph-set {:key index :style style} children])
"paragraph" "paragraph"
(let [style (sts/generate-paragraph-styles (clj->js node))] (let [style (sts/generate-paragraph-styles (clj->js node) props)]
[:p.paragraph {:key index :style style} children]) [:p.paragraph {:key index :style style} children])
nil))))) nil)))))

View file

@ -35,27 +35,29 @@
(= talign "justify") (obj/set! "justifyContent" "stretch")))) (= talign "justify") (obj/set! "justifyContent" "stretch"))))
(defn generate-paragraph-set-styles (defn generate-paragraph-set-styles
[data] [data props]
;; The position absolute is used so the paragraph is "outside" ;; The position absolute is used so the paragraph is "outside"
;; the normal layout and can grow outside its parent ;; the normal layout and can grow outside its parent
;; We use this element to measure the size of the text ;; We use this element to measure the size of the text
(let [base #js {:display "inline-block" (let [base #js {:display "inline-block"}]
:position "absolute"}]
base)) base))
(defn generate-paragraph-styles (defn generate-paragraph-styles
[data] [data props]
(let [base #js {:fontSize "14px" (let [shape (obj/get props "shape")
grow-type (:grow-type shape)
base #js {:fontSize "14px"
:margin "inherit" :margin "inherit"
:lineHeight "1.2"} :lineHeight "1.2"}
lh (obj/get data "line-height") lh (obj/get data "line-height")
ta (obj/get data "text-align")] ta (obj/get data "text-align")]
(cond-> base (cond-> base
ta (obj/set! "textAlign" ta) ta (obj/set! "textAlign" ta)
lh (obj/set! "lineHeight" lh)))) lh (obj/set! "lineHeight" lh)
(= grow-type :auto-width) (obj/set! "whiteSpace" "pre"))))
(defn generate-text-styles (defn generate-text-styles
[data] [data props]
(let [letter-spacing (obj/get data "letter-spacing") (let [letter-spacing (obj/get data "letter-spacing")
text-decoration (obj/get data "text-decoration") text-decoration (obj/get data "text-decoration")
text-transform (obj/get data "text-transform") text-transform (obj/get data "text-transform")
@ -82,7 +84,7 @@
fill-color-ref-file (obj/get data "fill-color-ref-file") fill-color-ref-file (obj/get data "fill-color-ref-file")
[r g b a] (uc/hex->rgba fill-color fill-opacity) [r g b a] (uc/hex->rgba fill-color fill-opacity)
background (if fill-color-gradient text-color (if fill-color-gradient
(uc/gradient->css (js->clj fill-color-gradient)) (uc/gradient->css (js->clj fill-color-gradient))
(str/format "rgba(%s, %s, %s, %s)" r g b a)) (str/format "rgba(%s, %s, %s, %s)" r g b a))
@ -91,7 +93,8 @@
base #js {:textDecoration text-decoration base #js {:textDecoration text-decoration
:textTransform text-transform :textTransform text-transform
:lineHeight (or line-height "inherit") :lineHeight (or line-height "inherit")
"--text-color" background}] :color text-color
"--text-color" text-color}]
(when (and (string? letter-spacing) (when (and (string? letter-spacing)
(pos? (alength letter-spacing))) (pos? (alength letter-spacing)))
@ -117,4 +120,5 @@
(obj/set! base "fontStyle" font-style) (obj/set! base "fontStyle" font-style)
(obj/set! base "fontWeight" font-weight)))) (obj/set! base "fontWeight" font-weight))))
base)) base))

View file

@ -156,10 +156,11 @@
(let [res-point (if (#{:top :bottom} position) (let [res-point (if (#{:top :bottom} position)
{:y y} {:y y}
{:x x}) {:x x})
width length #_(max 0 (- length (/ (* resize-point-rect-size 2) zoom))) target-length (max 0 (- length (/ (* resize-point-rect-size 2) zoom)))
width (if (< target-length 6) length target-length)
height (/ resize-side-height zoom)] height (/ resize-side-height zoom)]
[:rect {:x x [:rect {:x (+ x (/ (- length width) 2))
:y (- y (/ resize-side-height 2 zoom)) :y (- y (/ height 2))
:width width :width width
:height height :height height
:transform (gmt/multiply transform :transform (gmt/multiply transform

View file

@ -115,11 +115,13 @@
(resize-observer shape text-node ".paragraph-set") (resize-observer shape text-node ".paragraph-set")
[:> shape-container {:shape shape} [:> shape-container {:shape shape}
[:& text/text-shape {:key "text-shape" ;; We keep hidden the shape when we're editing so it keeps track of the size
:ref text-ref ;; and updates the selrect acordingly
:shape shape [:g.text-shape {:opacity (when edition? 0)}
:selected? selected? [:& text/text-shape {:key "text-shape"
:style {:display (when edition? "none")}}] :ref text-ref
:shape shape
:selected? selected?}]]
(when edition? (when edition?
[:& editor/text-shape-edit {:key "editor" [:& editor/text-shape-edit {:key "editor"
:shape shape}]) :shape shape}])

View file

@ -82,7 +82,7 @@
data (obj/get props "element") data (obj/get props "element")
type (obj/get data "type") type (obj/get data "type")
shape (obj/get props "shape") shape (obj/get props "shape")
style (sts/generate-paragraph-set-styles data) style (sts/generate-paragraph-set-styles data props)
attrs (-> (obj/get props "attributes") attrs (-> (obj/get props "attributes")
(obj/set! "style" style) (obj/set! "style" style)
(obj/set! "className" type))] (obj/set! "className" type))]
@ -95,7 +95,7 @@
childs (obj/get props "children") childs (obj/get props "children")
data (obj/get props "element") data (obj/get props "element")
type (obj/get data "type") type (obj/get data "type")
style (sts/generate-paragraph-styles data) style (sts/generate-paragraph-styles data props)
attrs (-> (obj/get props "attributes") attrs (-> (obj/get props "attributes")
(obj/set! "style" style) (obj/set! "style" style)
(obj/set! "className" type))] (obj/set! "className" type))]
@ -106,10 +106,14 @@
[props] [props]
(let [childs (obj/get props "children") (let [childs (obj/get props "children")
data (obj/get props "leaf") data (obj/get props "leaf")
style (sts/generate-text-styles data) type (obj/get data "type")
style (sts/generate-text-styles data props)
attrs (-> (obj/get props "attributes") attrs (-> (obj/get props "attributes")
(obj/set! "style" style) (obj/set! "style" style))
(obj/set! "className" "text-node"))] gradient (obj/get data "fill-color-gradient" nil)]
(if gradient
(obj/set! attrs "className" (str type " gradient"))
(obj/set! attrs "className" type))
[:> :span attrs childs])) [:> :span attrs childs]))
(defn- render-element (defn- render-element
@ -135,7 +139,7 @@
;; --- Text Shape Edit ;; --- Text Shape Edit
(mf/defc text-shape-edit (mf/defc text-shape-edit-html
{::mf/wrap [mf/memo] {::mf/wrap [mf/memo]
::mf/wrap-props false ::mf/wrap-props false
::mf/forward-ref true} ::mf/forward-ref true}
@ -161,9 +165,6 @@
on-click-outside on-click-outside
(fn [event] (fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(let [sidebar (dom/get-element "settings-bar") (let [sidebar (dom/get-element "settings-bar")
assets (dom/get-element-by-class "assets-bar") assets (dom/get-element-by-class "assets-bar")
cpicker (dom/get-element-by-class "colorpicker-tooltip") cpicker (dom/get-element-by-class "colorpicker-tooltip")
@ -174,9 +175,13 @@
(and assets (.contains assets target)) (and assets (.contains assets target))
(and self (.contains self target)) (and self (.contains self target))
(and cpicker (.contains cpicker target))) (and cpicker (.contains cpicker target)))
(if selecting? (do
(mf/set-ref-val! selecting-ref false) (dom/prevent-default event)
(on-close))))) (dom/stop-propagation event)
(if selecting?
(mf/set-ref-val! selecting-ref false)
(on-close))))))
on-mouse-down on-mouse-down
(fn [event] (fn [event]
@ -230,13 +235,9 @@
(reset! state (parse-content content)) (reset! state (parse-content content))
(reset! content-var content))) (reset! content-var content)))
[:foreignObject {:ref self-ref [:div.text-editor {:ref self-ref}
:transform (gsh/transform-matrix shape)
:x x :y y
:width (if (#{:auto-width} grow-type) 10000 width)
:height (if (#{:auto-height :auto-width} grow-type) 10000 height)}
[:style "span { line-height: inherit; } [:style "span { line-height: inherit; }
.text-node { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"] .gradient { background: var(--text-color); -webkit-text-fill-color: transparent; -webkit-background-clip: text;"]
[:> rslate/Slate {:editor editor [:> rslate/Slate {:editor editor
:value @state :value @state
:on-change on-change} :on-change on-change}
@ -257,3 +258,17 @@
;; WARN: monky patch ;; WARN: monky patch
(obj/set! slate/Transforms "deselect" (constantly nil))) (obj/set! slate/Transforms "deselect" (constantly nil)))
:placeholder (when (= :fixed grow-type) "Type some text here...")}]]])) :placeholder (when (= :fixed grow-type) "Type some text here...")}]]]))
(mf/defc text-shape-edit
{::mf/wrap [mf/memo]
::mf/wrap-props false
::mf/forward-ref true}
[props ref]
(let [shape (unchecked-get props "shape")
{:keys [x y width height grow-type]} shape]
[:foreignObject {:transform (gsh/transform-matrix shape)
:x x :y y
:width (if (#{:auto-width} grow-type) 10000 width)
:height (if (#{:auto-height :auto-width} grow-type) 10000 height)}
[:& text-shape-edit-html {:shape shape}]]))

View file

@ -116,8 +116,7 @@
(st/emit! dw/start-pan) (st/emit! dw/start-pan)
(rx/subscribe stream (rx/subscribe stream
(fn [delta] (fn [delta]
(let [vbox (.. ^js node -viewBox -baseVal) (let [zoom (gpt/point @refs/selected-zoom)
zoom (gpt/point @refs/selected-zoom)
delta (gpt/divide delta zoom)] delta (gpt/divide delta zoom)]
(st/emit! (dw/update-viewport-position (st/emit! (dw/update-viewport-position
{:x #(- % (:x delta)) {:x #(- % (:x delta))
@ -352,10 +351,15 @@
on-mouse-move on-mouse-move
(fn [event] (fn [event]
(let [event (.getBrowserEvent ^js event) (let [event (.getBrowserEvent ^js event)
pt (dom/get-client-position ^js event) raw-pt (dom/get-client-position ^js event)
pt (translate-point-to-viewport pt) pt (translate-point-to-viewport raw-pt)
delta (gpt/point (.-movementX ^js event)
(.-movementY ^js event))] ;; We calculate the delta because Safari's MouseEvent.movementX/Y drop
;; events
delta (if @last-position
(gpt/subtract raw-pt @last-position)
(gpt/point 0 0))]
(reset! last-position raw-pt)
(st/emit! (ms/->PointerEvent :delta delta (st/emit! (ms/->PointerEvent :delta delta
(kbd/ctrl? event) (kbd/ctrl? event)
(kbd/shift? event) (kbd/shift? event)

View file

@ -245,3 +245,6 @@
(defn ^boolean class? [node class-name] (defn ^boolean class? [node class-name]
(let [class-list (.-classList ^js node)] (let [class-list (.-classList ^js node)]
(.contains ^js class-list class-name))) (.contains ^js class-list class-name)))
(defn get-user-agent []
(.-userAgent js/navigator))