diff --git a/backend/src/app/loggers/mattermost.clj b/backend/src/app/loggers/mattermost.clj index fc0109bdda..f4a60e5c1f 100644 --- a/backend/src/app/loggers/mattermost.clj +++ b/backend/src/app/loggers/mattermost.clj @@ -60,7 +60,7 @@ (a/close! output))) (defn- send-mattermost-notification! - [cfg {:keys [host version id] :as cdata}] + [cfg {:keys [host id] :as cdata}] (try (let [uri (:uri cfg) text (str "Unhandled exception (host: " host ", url: " (cfg/get :public-uri) "/dbg/error-by-id/" id "\n" diff --git a/common/src/app/common/pages/migrations.cljc b/common/src/app/common/pages/migrations.cljc index f06c79563f..104e1fe4c5 100644 --- a/common/src/app/common/pages/migrations.cljc +++ b/common/src/app/common/pages/migrations.cljc @@ -258,7 +258,7 @@ (defmethod migrate 11 [data] - (letfn [(update-object [objects id shape] + (letfn [(update-object [objects _id shape] (if (= :frame (:type shape)) (d/update-when shape :shapes (fn [shapes] (filterv (fn [id] (contains? objects id)) shapes))) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 390e415db4..1163a56873 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -203,8 +203,11 @@ (when-not (some? (get-in state [:workspace-editor-state id])) (let [objects (wsh/lookup-page-objects state) shape (get objects id) + update-node? (fn [node] + (or (txt/is-text-node? node) + (txt/is-paragraph-node? node))) - update-fn #(update-shape % txt/is-text-node? attrs/merge attrs) + update-fn #(update-shape % update-node? attrs/merge attrs) shape-ids (cond (= (:type shape) :text) [id] (= (:type shape) :group) (cp/get-children id objects))] (rx/of (dch/update-shapes shape-ids update-fn))))))) @@ -325,8 +328,8 @@ (ptk/reify ::save-font ptk/UpdateEvent (update [_ state] - ;; Check if the data has any multiple - (assoc-in state - [:workspace-local :defaults :font] - data)))) + (let [multiple? (->> data vals (d/seek #(= % :multiple)))] + (cond-> state + (not multiple?) + (assoc-in [:workspace-local :defaults :font] data)))))) diff --git a/frontend/src/app/main/ui/components/editable_select.cljs b/frontend/src/app/main/ui/components/editable_select.cljs index 40c13e7702..127e0e403e 100644 --- a/frontend/src/app/main/ui/components/editable_select.cljs +++ b/frontend/src/app/main/ui/components/editable_select.cljs @@ -14,19 +14,23 @@ [app.util.timers :as timers] [rumext.alpha :as mf])) -(mf/defc editable-select [{:keys [value type options class on-change placeholder]}] +(mf/defc editable-select [{:keys [value type options class on-change placeholder on-blur]}] (let [state (mf/use-state {:id (uuid/next) :is-open? false :current-value value :top nil :left nil :bottom nil}) + + emit-blur? (mf/use-ref nil) + open-dropdown #(swap! state assoc :is-open? true) close-dropdown #(swap! state assoc :is-open? false) select-item (fn [value] (fn [_] (swap! state assoc :current-value value) - (when on-change (on-change value)))) + (when on-change (on-change value)) + (when on-blur (on-blur)))) as-key-value (fn [item] (if (map? item) [(:value item) (:label item)] [item item])) @@ -55,21 +59,38 @@ assoc :left left :top top - :bottom bottom))))))] + :bottom bottom)))))) + + handle-focus + (mf/use-callback + (fn [] + (mf/set-ref-val! emit-blur? false))) + + handle-blur + (mf/use-callback + (fn [] + (mf/set-ref-val! emit-blur? true) + (timers/schedule + 200 + (fn [] + (when (and on-blur (mf/ref-val emit-blur?)) (on-blur))))))] (mf/use-effect - (mf/deps value) - #(reset! state {:current-value value})) + (mf/deps value (:current-value @state)) + #(when (not= value (:current-value @state)) + (reset! state {:current-value value}))) (mf/use-effect - (mf/deps options) - #(reset! state {:is-open? false - :current-value value})) + (mf/deps (:is-open? @state)) + (fn [] + (mf/set-ref-val! emit-blur? (not (:is-open? @state))))) [:div.editable-select {:class class :ref on-node-load} [:input.input-text {:value (or (-> @state :current-value value->label) "") :on-change handle-change-input + :on-focus handle-focus + :on-blur handle-blur :placeholder placeholder :type type}] [:span.dropdown-button {:on-click open-dropdown} i/arrow-down] 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 26617eb3e3..e2321b4de6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs @@ -157,11 +157,11 @@ on-change (mf/use-callback (fn [val] - (let [val (handle-change val)] - (let [val (if (true? @blured) - (ted/add-editor-blur-selection val) - (ted/remove-editor-blur-selection val))] - (st/emit! (dwt/update-editor-state shape val)))))) + (let [val (handle-change val) + val (if (true? @blured) + (ted/add-editor-blur-selection val) + (ted/remove-editor-blur-selection val))] + (st/emit! (dwt/update-editor-state shape val))))) on-editor (mf/use-callback diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs index ac2dc026fe..5f55a9ab2e 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/text.cljs @@ -20,6 +20,7 @@ [app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry typography-options]] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] + [app.util.timers :as tm] [cuerdas.core :as str] [rumext.alpha :as mf])) @@ -80,11 +81,12 @@ (def attrs (d/concat #{} shape-attrs root-attrs paragraph-attrs text-attrs)) (mf/defc text-align-options - [{:keys [values on-change] :as props}] + [{:keys [values on-change on-blur] :as props}] (let [{:keys [text-align]} values handle-change (fn [_ new-align] - (on-change {:text-align new-align}))] + (on-change {:text-align new-align}) + (when (some? on-blur) (on-blur)))] ;; --- Align [:div.align-icons @@ -110,10 +112,12 @@ i/text-align-justify]])) (mf/defc text-direction-options - [{:keys [values on-change] :as props}] + [{:keys [values on-change on-blur] :as props}] (let [direction (:text-direction values) - handle-change (fn [_ val] - (on-change {:text-direction val}))] + handle-change + (fn [_ val] + (on-change {:text-direction val}) + (when (some? on-blur) (on-blur)))] ;; --- Align [:div.align-icons [:span.tooltip.tooltip-bottom-left @@ -128,12 +132,13 @@ i/text-direction-rtl]])) (mf/defc vertical-align - [{:keys [values on-change] :as props}] + [{:keys [values on-change on-blur] :as props}] (let [{:keys [vertical-align]} values vertical-align (or vertical-align "top") handle-change (fn [_ new-align] - (on-change {:vertical-align new-align}))] + (on-change {:vertical-align new-align}) + (when (some? on-blur) (on-blur)))] [:div.align-icons [:span.tooltip.tooltip-bottom-left @@ -153,11 +158,12 @@ i/align-bottom]])) (mf/defc grow-options - [{:keys [ids values] :as props}] + [{:keys [ids values on-blur] :as props}] (let [grow-type (:grow-type values) handle-change-grow (fn [_ grow-type] - (st/emit! (dch/update-shapes ids #(assoc % :grow-type grow-type))))] + (st/emit! (dch/update-shapes ids #(assoc % :grow-type grow-type))) + (when (some? on-blur) (on-blur)))] [:div.align-icons [:span.tooltip.tooltip-bottom @@ -177,11 +183,12 @@ i/auto-height]])) (mf/defc text-decoration-options - [{:keys [values on-change] :as props}] + [{:keys [values on-change on-blur] :as props}] (let [text-decoration (or (:text-decoration values) "none") handle-change (fn [_ type] - (on-change {:text-decoration type}))] + (on-change {:text-decoration type}) + (when (some? on-blur) (on-blur)))] [:div.align-icons [:span.tooltip.tooltip-bottom {:alt (tr "workspace.options.text-options.none") @@ -222,7 +229,8 @@ (mf/use-callback (mf/deps values) (fn [id attrs] - (st/emit! (dwt/save-font (merge txt/default-text-attrs values attrs))) + (st/emit! (dwt/save-font (-> (merge txt/default-text-attrs values attrs) + (select-keys text-attrs)))) (let [attrs (select-keys attrs root-attrs)] (when-not (empty? attrs) @@ -290,7 +298,15 @@ opts #js {:ids ids :values values - :on-change on-change}] + :on-change on-change + :on-blur + (fn [] + (tm/schedule + 100 + (fn [] + (when (not= "INPUT" (-> (dom/get-active) (dom/get-tag-name))) + (let [node (dom/get-element-by-class "public-DraftEditor-content")] + (dom/focus! node))))))}] [:div.element-set [:div.element-set-title 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 ca5e77631a..68c503fcca 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 @@ -230,7 +230,7 @@ :current? (= (:id font) (:id selected))}]))) (mf/defc font-options - [{:keys [values on-change] :as props}] + [{:keys [values on-change on-blur] :as props}] (let [{:keys [font-id font-size font-variant-id]} values font-id (or font-id (:font-id txt/default-text-attrs)) @@ -271,18 +271,26 @@ :font-family (:family font) :font-variant-id new-variant-id :font-weight (:weight variant) - :font-style (:style variant)})))) + :font-style (:style variant)}) + (dom/blur! (dom/get-target event))))) on-font-select (mf/use-callback (mf/deps change-font) (fn [font*] (when (not= font font*) - (change-font (:id font*))))) + (change-font (:id font*))) + + (when (some? on-blur) + (on-blur)))) on-font-selector-close (mf/use-callback - #(reset! open-selector? false))] + (fn [] + (reset! open-selector? false) + (when (some? on-blur) + (on-blur)) + ))] [:* (when @open-selector? @@ -314,12 +322,14 @@ :options size-options :type "number" :placeholder "--" - :on-change on-font-size-change}]) + :on-change on-font-size-change + :on-blur on-blur}]) [:select.input-select.variant-option {:disabled (= font-id :multiple) :value (attr->string font-variant-id) - :on-change on-font-variant-change} + :on-change on-font-variant-change + :on-blur on-blur} (when (or (= font-id :multiple) (= font-variant-id :multiple)) [:option {:value ""} "--"]) (for [variant (:variants font)] @@ -329,7 +339,7 @@ (mf/defc spacing-options - [{:keys [values on-change] :as props}] + [{:keys [values on-change on-blur] :as props}] (let [{:keys [line-height letter-spacing]} values @@ -353,7 +363,8 @@ :max "200" :value (attr->string line-height) :placeholder (tr "settings.multiple") - :on-change #(handle-change % :line-height)}]] + :on-change #(handle-change % :line-height) + :on-blur on-blur}]] [:div.input-icon [:span.icon-before.tooltip.tooltip-bottom @@ -366,18 +377,21 @@ :max "200" :value (attr->string letter-spacing) :placeholder (tr "settings.multiple") - :on-change #(handle-change % :letter-spacing)}]]])) + :on-change #(handle-change % :letter-spacing) + :on-blur on-blur}]]])) (mf/defc text-transform-options - [{:keys [values on-change] :as props}] + [{:keys [values on-change on-blur] :as props}] (let [text-transform (or (:text-transform values) "none") handle-change (fn [_ type] - (on-change {:text-transform type}))] + (on-change {:text-transform type}) + (when (some? on-blur) (on-blur)))] [:div.align-icons [:span.tooltip.tooltip-bottom {:alt (tr "workspace.options.text-options.none") :class (dom/classnames :current (= "none" text-transform)) + :focus #(dom/prevent-default %) :on-click #(handle-change % "none")} i/minus] [:span.tooltip.tooltip-bottom @@ -397,11 +411,12 @@ i/titlecase]])) (mf/defc typography-options - [{:keys [ids editor values on-change]}] + [{:keys [ids editor values on-change on-blur]}] (let [opts #js {:editor editor :ids ids :values values - :on-change on-change}] + :on-change on-change + :on-blur on-blur}] [:div.element-set-content [:> font-options opts] [:div.row-flex diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs index a15c66e02a..a8eccfa4d5 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs @@ -9,10 +9,9 @@ [app.common.data :as d] [app.main.data.workspace.texts :as dwt] [app.main.refs :as refs] - [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-attrs]] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] - [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu]] + [app.main.ui.workspace.sidebar.options.menus.fill :refer [fill-menu fill-attrs]] [app.main.ui.workspace.sidebar.options.menus.layer :refer [layer-attrs layer-menu]] [app.main.ui.workspace.sidebar.options.menus.measures :refer [measure-attrs measures-menu]] [app.main.ui.workspace.sidebar.options.menus.shadow :refer [shadow-menu]] diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index f05f2a1a5f..dfa4f4467a 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -223,11 +223,13 @@ (defn focus! [node] - (.focus node)) + (when (some? node) + (.focus node))) (defn blur! [node] - (.blur node)) + (when (some? node) + (.blur node))) (defn fullscreen? [] @@ -291,8 +293,11 @@ (defn get-user-agent [] (.-userAgent globals/navigator)) +(defn get-active [] + (.-activeElement globals/document)) + (defn active? [node] - (= (.-activeElement globals/document) node)) + (= (get-active) node)) (defn get-data [^js node ^string attr] (.getAttribute node (str "data-" attr))) diff --git a/frontend/src/app/util/text_editor_impl.js b/frontend/src/app/util/text_editor_impl.js index 3c735b97cf..18de4ec8f0 100644 --- a/frontend/src/app/util/text_editor_impl.js +++ b/frontend/src/app/util/text_editor_impl.js @@ -34,7 +34,7 @@ function mergeBlockData(block, newData) { } } - return block.merge({ + return block.mergeDeep({ data: data }); } @@ -119,29 +119,31 @@ export function updateCurrentBlockData(state, attrs) { } export function applyInlineStyle(state, styles) { - let selection = state.getSelection(); + const userSelection = state.getSelection(); + let selection = userSelection; if (selection.isCollapsed()) { selection = getSelectAllSelection(state); } + let result = state; let content = null; for (let style of styles) { const [p, k, v] = style.split("$$$"); const prefix = [p, k, ""].join("$$$"); - content = state.getCurrentContent(); + content = result.getCurrentContent(); content = removeInlineStylePrefix(content, selection, prefix); if (v !== "z:null") { content = Modifier.applyInlineStyle(content, selection, style); } - state = EditorState.push(state, content, "change-inline-style"); + result = EditorState.push(result, content, "change-inline-style"); } - return state; + return EditorState.acceptSelection(result, userSelection); } export function splitBlockPreservingData(state) { @@ -308,7 +310,7 @@ export function updateBlockData(state, blockKey, data) { ); const result = EditorState.push(state, newContent, 'change-block-data'); - return EditorState.acceptSelection(result, userSelection) + return EditorState.acceptSelection(result, userSelection); } export function getSelection(state) {