diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 42150c4af1..81b2236da2 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1243,6 +1243,24 @@ (update [_ state] (reduce #(update %1 :workspace-preview-blend dissoc %2) state ids)))) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Preview fonts +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn set-preview-font + [ids font-data] + (ptk/reify ::set-preview-font + ptk/UpdateEvent + (update [_ state] + (reduce #(assoc-in %1 [:workspace-preview-font %2] font-data) state ids)))) + +(defn unset-preview-font + [ids] + (ptk/reify ::unset-preview-font + ptk/UpdateEvent + (update [_ state] + (reduce #(update %1 :workspace-preview-font dissoc %2) state ids)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Components ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1386,3 +1404,9 @@ (dm/export dwpg/duplicate-page) (dm/export dwpg/rename-page) (dm/export dwpg/delete-page) + +;; Preview +;; (dm/export set-preview-blend-mode) +;; (dm/export unset-preview-blend-mode) +;; (dm/export set-preview-font) +;; (dm/export unset-preview-font) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index fcf22ed020..26e7ecec01 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -634,6 +634,12 @@ (defn workspace-preview-blend-by-id [id] (l/derived (l/key id) workspace-preview-blend =)) +(def workspace-preview-font + (l/derived :workspace-preview-font st/state)) + +(defn workspace-preview-font-by-id [id] + (l/derived (l/key id) workspace-preview-font =)) + (def specialized-panel (l/derived :specialized-panel st/state)) diff --git a/frontend/src/app/main/ui/shapes/text/fo_text.cljs b/frontend/src/app/main/ui/shapes/text/fo_text.cljs index 1f7836cc0c..87f6d8f47a 100644 --- a/frontend/src/app/main/ui/shapes/text/fo_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/fo_text.cljs @@ -22,9 +22,10 @@ parent (obj/get props "parent") shape (obj/get props "shape") text (:text node) + shape-id (:id shape) style (if (= text "") - (sts/generate-text-styles shape parent) - (sts/generate-text-styles shape node))] + (sts/generate-text-styles shape-id parent) + (sts/generate-text-styles shape-id node))] [:span.text-node {:style style} (if (= text "") "\u00A0" text)])) diff --git a/frontend/src/app/main/ui/shapes/text/html_text.cljs b/frontend/src/app/main/ui/shapes/text/html_text.cljs index fd3995c38e..2edc44305c 100644 --- a/frontend/src/app/main/ui/shapes/text/html_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/html_text.cljs @@ -13,6 +13,12 @@ [app.util.object :as obj] [rumext.v2 :as mf])) +(defn generate-styles + [shape parent node] + (let [shape-id (:id shape)] + {:parent (sts/generate-text-styles shape-id parent) + :node (sts/generate-text-styles shape-id node)})) + (mf/defc render-text {::mf/wrap-props false} [props] diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index 67c3a03562..0ca00c31c9 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -14,8 +14,10 @@ [app.main.ui.formats :as fmt] [app.util.color :as uc] [app.util.object :as obj] - [cuerdas.core :as str])) - + [cuerdas.core :as str] + [app.main.refs :as refs] + [rumext.v2 :as mf])) + (defn generate-root-styles ([props node] (generate-root-styles props node false)) @@ -66,88 +68,104 @@ (some? text-align) (obj/set! "textAlign" text-align)))) (defn generate-text-styles - ([shape data] - (generate-text-styles shape data nil)) - - ([{:keys [grow-type] :as shape} data {:keys [show-text?] :or {show-text? true}}] + ([shape-id data show-text? grow-type preview-font] (let [letter-spacing (:letter-spacing data 0) - text-decoration (:text-decoration data) - text-transform (:text-transform data) + text-decoration (:text-decoration data) + text-transform (:text-transform data) - font-id (or (:font-id data) - (:font-id txt/default-text-attrs)) + font-id (or (:font-id data) + (:font-id txt/default-text-attrs)) - font-variant-id (:font-variant-id data) + font-variant-id (:font-variant-id data) - font-size (:font-size data) + font-size (:font-size data) - fill-color (or (-> data :fills first :fill-color) (:fill-color data)) - fill-opacity (or (-> data :fills first :fill-opacity) (:fill-opacity data)) - fill-gradient (or (-> data :fills first :fill-color-gradient) (:fill-color-gradient data)) + fill-color (or (-> data :fills first :fill-color) (:fill-color data)) + fill-opacity (or (-> data :fills first :fill-opacity) (:fill-opacity data)) + fill-gradient (or (-> data :fills first :fill-color-gradient) (:fill-color-gradient data)) - [r g b a] (cc/hex->rgba fill-color fill-opacity) - text-color (when (and (some? fill-color) (some? fill-opacity)) - (str/format "rgba(%s, %s, %s, %s)" r g b a)) + [r g b a] (cc/hex->rgba fill-color fill-opacity) + text-color (when (and (some? fill-color) (some? fill-opacity)) + (str/format "rgba(%s, %s, %s, %s)" r g b a)) - gradient? (some? fill-gradient) + gradient? (some? fill-gradient) - text-color (if gradient? - (uc/color->background {:gradient fill-gradient}) - text-color) + text-color (if gradient? + (uc/color->background {:gradient fill-gradient}) + text-color) - fontsdb (deref fonts/fontsdb) + fontsdb (deref fonts/fontsdb) - base #js {:textDecoration text-decoration - :textTransform text-transform - :fontSize font-size - :color (if (and show-text? (not gradient?)) text-color "transparent") - :background (when (and show-text? gradient?) text-color) - :caretColor (if (and (not gradient?) text-color) text-color "black") - :overflowWrap "initial" - :lineBreak "auto" - :whiteSpace "break-spaces" - :textRendering "geometricPrecision"} - fills - (cond - ;; DEPRECATED: still here for backward compatibility with - ;; old penpot files that still has a single color. - (or (some? (:fill-color data)) - (some? (:fill-opacity data)) - (some? (:fill-color-gradient data))) - [(d/without-nils (select-keys data [:fill-color :fill-opacity :fill-color-gradient - :fill-color-ref-id :fill-color-ref-file]))] + _ (js/console.log "Preview font for" shape-id ":" preview-font) - (nil? (:fills data)) - [{:fill-color "#000000" :fill-opacity 1}] + base #js {:textDecoration text-decoration + :textTransform text-transform + :fontSize font-size + :color (if (and show-text? (not gradient?)) text-color "transparent") + :background (when (and show-text? gradient?) text-color) + :caretColor (if (and (not gradient?) text-color) text-color "black") + :overflowWrap "initial" + :lineBreak "auto" + :whiteSpace "break-spaces" + :textRendering "geometricPrecision"} + fills + (cond + ;; DEPRECATED: still here for backward compatibility with + ;; old penpot files that still has a single color. + (or (some? (:fill-color data)) + (some? (:fill-opacity data)) + (some? (:fill-color-gradient data))) + [(d/without-nils (select-keys data [:fill-color :fill-opacity :fill-color-gradient + :fill-color-ref-id :fill-color-ref-file]))] - :else - (:fills data)) + (nil? (:fills data)) + [{:fill-color "#000000" :fill-opacity 1}] - font (some->> font-id (get fontsdb)) + :else + (:fills data)) - [font-family font-style font-weight] - (when (some? font) - (let [font-variant (d/seek #(= font-variant-id (:id %)) (:variants font))] - [(str/quote (or (:family font) (:font-family data))) - (or (:style font-variant) (:font-style data)) - (or (:weight font-variant) (:font-weight data))])) + font (some->> font-id (get fontsdb)) - base (obj/set! base "--font-id" font-id)] + [font-family font-style font-weight] + (if (some? preview-font) + ;; Use preview font data + [(str/quote (:font-family preview-font)) + (:font-style preview-font) + (:font-weight preview-font)] + ;; Use regular font data + (when (some? font) + (let [font-variant (d/seek #(= font-variant-id (:id %)) (:variants font))] + [(str/quote (or (:family font) (:font-family data))) + (or (:style font-variant) (:font-style data)) + (or (:weight font-variant) (:font-weight data))]))) - (cond-> base - (some? fills) - (obj/set! "--fills" (transit/encode-str fills)) + base (obj/set! base "--font-id" font-id)] - (and (string? letter-spacing) (pos? (alength letter-spacing))) - (obj/set! "letterSpacing" (str letter-spacing "px")) + (cond-> base + (some? fills) + (obj/set! "--fills" (transit/encode-str fills)) - (and (string? font-size) (pos? (alength font-size))) - (obj/set! "fontSize" (str font-size "px")) + (and (string? letter-spacing) (pos? (alength letter-spacing))) + (obj/set! "letterSpacing" (str letter-spacing "px")) - (some? font) - (-> (obj/set! "fontFamily" font-family) - (obj/set! "fontStyle" font-style) - (obj/set! "fontWeight" font-weight)) + (and (string? font-size) (pos? (alength font-size))) + (obj/set! "fontSize" (str font-size "px")) - (= grow-type :auto-width) - (obj/set! "whiteSpace" "pre"))))) + (some? font) + (-> (obj/set! "fontFamily" font-family) + (obj/set! "fontStyle" font-style) + (obj/set! "fontWeight" font-weight)) + + (= grow-type :auto-width) + (obj/set! "whiteSpace" "pre")))) + + ;; Backward compatibility + ([shape data] + (let [shape-id (:id shape) + preview-font (mf/deref (refs/workspace-preview-font-by-id shape-id))] + (generate-text-styles shape-id data true nil preview-font))) + + ([shape data {:keys [show-text?] :or {show-text? true}}] + (let [shape-id (:id shape) + preview-font (mf/deref (refs/workspace-preview-font-by-id shape-id))] + (generate-text-styles shape-id data show-text? nil preview-font)))) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 874877ef21..334edbbad6 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -16,7 +16,8 @@ [app.main.ui.shapes.fills :as fills] [app.main.ui.shapes.gradients :as grad] [app.util.object :as obj] - [rumext.v2 :as mf])) + [rumext.v2 :as mf] + [app.main.refs :as refs])) (def fill-attrs [:fill-color :fill-color-gradient :fill-opacity]) @@ -42,6 +43,8 @@ {:keys [x y width height position-data]} shape + preview-font (mf/deref (app.main.refs/workspace-preview-font-by-id (:id shape))) + transform (gsh/transform-str shape) ;; These position attributes are not really necessary but they are convenient for for the export @@ -73,6 +76,14 @@ (for [[index data] (d/enumerate position-data)] (let [rtl? (= "rtl" (:direction data)) + ;; --- PREVIEW FONT OVERRIDE --- + data (if preview-font + (merge data + {:font-family (:font-family preview-font) + :font-weight (:font-weight preview-font) + :font-style (:font-style preview-font)}) + data) + browser-props (cond (cf/check-browser? :safari) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs index 8fe5c89200..2eb8b6e6d5 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs @@ -66,7 +66,7 @@ (.toJS) (js->clj :keywordize-keys true)) (txt/styles-to-attrs styles))] - (sts/generate-text-styles shape data {:show-text? false}))) + (sts/generate-text-styles (:id shape) data {:show-text? false}))) (def default-decorator (ted/create-decorator "PENPOT_SELECTION" selection-component)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index 6f4db43e23..9f24680ce8 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -16,6 +16,7 @@ [app.main.data.common :as dcm] [app.main.data.fonts :as fts] [app.main.data.shortcuts :as dsc] + [app.main.data.workspace :as dw] [app.main.features :as features] [app.main.fonts :as fonts] [app.main.refs :as refs] @@ -60,9 +61,17 @@ (mf/defc font-item* {::mf/wrap [mf/memo]} - [{:keys [font is-current on-click style]}] + [{:keys [font is-current on-click style on-hover on-leave]}] (let [item-ref (mf/use-ref) - on-click (mf/use-fn (mf/deps font) #(on-click font))] + on-click (mf/use-fn (mf/deps font) #(on-click font)) + on-mouse-enter (mf/use-fn (mf/deps font on-hover) + (fn [] + (js/console.log "Mouse enter on font" (:name font)) + (when on-hover (on-hover font)))) + on-mouse-leave (mf/use-fn (mf/deps on-leave) + (fn [] + (js/console.log "Mouse leave on font") + (when on-leave (on-leave))))] (mf/use-effect (mf/deps is-current) @@ -75,7 +84,9 @@ [:div {:class (stl/css :font-wrapper) :style style :ref item-ref - :on-click on-click} + :on-click on-click + :on-mouse-enter on-mouse-enter + :on-mouse-leave on-mouse-leave} [:div {:class (stl/css-case :font-item true :selected is-current)} [:span {:class (stl/css :label)} (:name font)] @@ -95,7 +106,7 @@ (into [] xform fonts))) (mf/defc font-selector* - [{:keys [on-select on-close current-font show-recent full-size]}] + [{:keys [on-select on-close current-font show-recent full-size ids]}] (let [selected (mf/use-state current-font) state* (mf/use-state #(do {:term "" :backends #{}})) @@ -112,9 +123,14 @@ recent-fonts (mf/with-memo [state recent-fonts] (filter-fonts state recent-fonts)) - full-size? (boolean (and full-size show-recent)) + ;; --- NEW: auto-select first font on search --- + _auto-select-first + (mf/with-effect [(:term state) fonts] + (when (and (seq fonts) (not (str/blank? (:term state)))) + (reset! selected (first fonts)))) + select-next (mf/use-fn (mf/deps fonts) @@ -133,13 +149,13 @@ on-key-down (mf/use-fn - (mf/deps fonts) + (mf/deps fonts selected) (fn [event] (cond (kbd/up-arrow? event) (select-prev event) (kbd/down-arrow? event) (select-next event) (kbd/esc? event) (on-close) - (kbd/enter? event) (on-close) + (kbd/enter? event) (do (on-select @selected) (on-close)) :else (dom/focus! (mf/ref-val input))))) on-filter-change @@ -152,7 +168,36 @@ (mf/deps on-select on-close) (fn [font] (on-select font) - (on-close)))] + (on-close))) + + ;; Preview font handlers + on-font-hover + (mf/use-fn + (mf/deps ids) + (fn [font] + (js/console.log "Font hover triggered" font ids) + (when (and ids (seq ids)) + (let [{:keys [family] :as font-data} font + {:keys [id name weight style]} (fonts/get-default-variant font)] + (js/console.log "Setting preview font" {:font-id (:id font) + :font-family family + :font-variant-id (or id name) + :font-weight weight + :font-style style}) + (st/emit! (dw/set-preview-font ids {:font-id (:id font) + :font-family family + :font-variant-id (or id name) + :font-weight weight + :font-style style})))))) + + on-font-leave + (mf/use-fn + (mf/deps ids) + (fn [_] + (js/console.log "Font leave triggered" ids) + (when (and ids (seq ids)) + (js/console.log "Unsetting preview font") + (st/emit! (dw/unset-preview-font ids)))))] (mf/with-effect [fonts] (let [key (events/listen js/document "keydown" on-key-down)] @@ -163,8 +208,9 @@ (when-let [index (:index @selected)] (.scrollToRow ^js inst index)))) - (mf/with-effect [@selected] - (on-select @selected)) + ;; Removed the effect that was calling on-select automatically + ;; (mf/with-effect [@selected] + ;; (on-select @selected)) (mf/with-effect [] (st/emit! (dsc/push-shortcuts :typography {})) @@ -193,6 +239,8 @@ :font font :style {} :on-click on-select-and-close + :on-hover on-font-hover + :on-leave on-font-leave :is-current (= (:id font) (:id @selected))}])])] [:div {:class (stl/css-case :fonts-list true @@ -201,7 +249,7 @@ (fn [props] (let [width (unchecked-get props "width") height (unchecked-get props "height") - render #(row-renderer fonts @selected on-select-and-close %)] + render #(row-renderer fonts @selected on-select-and-close on-font-hover on-font-leave %)] (mf/html [:> rvt/List #js {:height height :ref flist @@ -211,7 +259,7 @@ :rowRenderer render}])))]]]])) (defn row-renderer - [fonts selected on-select props] + [fonts selected on-select on-hover on-leave props] (let [index (unchecked-get props "index") key (unchecked-get props "key") style (unchecked-get props "style") @@ -221,11 +269,13 @@ :font font :style style :on-click on-select + :on-hover on-hover + :on-leave on-leave :is-current (= (:id font) (:id selected))}]))) (mf/defc font-options {::mf/wrap-props false} - [{:keys [values on-change on-blur show-recent full-size-selector]}] + [{:keys [values on-change on-blur show-recent full-size-selector ids]}] (let [{:keys [font-id font-size font-variant-id]} values font-id (or font-id (:font-id txt/default-text-attrs)) @@ -297,7 +347,8 @@ :on-close on-font-selector-close :on-select on-font-select :full-size full-size-selector - :show-recent show-recent}]) + :show-recent show-recent + :ids ids}]) [:div {:class (stl/css :font-option) :title (tr "inspect.attributes.typography.font-family") diff --git a/frontend/src/app/util/code_gen/style_css.cljs b/frontend/src/app/util/code_gen/style_css.cljs index 5cef9d456a..59171acb60 100644 --- a/frontend/src/app/util/code_gen/style_css.cljs +++ b/frontend/src/app/util/code_gen/style_css.cljs @@ -220,7 +220,7 @@ body { (:paragraph "paragraph") (sts/generate-paragraph-styles shape node) - (sts/generate-text-styles shape node))] + (sts/generate-text-styles (:id shape) node))] (dm/fmt ".% {\n%\n}" (dm/str shape-selector " ." (:$id node))