From 1134f16ffac65bb00b9d7ccaf845c9410867d433 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Wed, 6 Mar 2024 12:46:30 +0100 Subject: [PATCH] :lipstick: Add cosmetic refactor to dashboard fonts react components --- frontend/src/app/main/ui/dashboard/fonts.cljs | 352 +++++++++++------- 1 file changed, 208 insertions(+), 144 deletions(-) diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index bdd8e5ede..a444236ce 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -25,108 +25,143 @@ [cuerdas.core :as str] [rumext.v2 :as mf])) -(defn- use-set-page-title +(defn- use-page-title [team section] - (mf/use-effect - (mf/deps team) - (fn [] - (when team - (let [tname (if (:is-default team) - (tr "dashboard.your-penpot") - (:name team))] - (case section - :fonts (dom/set-html-title (tr "title.dashboard.fonts" tname)) - :providers (dom/set-html-title (tr "title.dashboard.font-providers" tname)))))))) + (mf/with-effect [team] + (when team + (let [tname (if (:is-default team) + (tr "dashboard.your-penpot") + (:name team))] + (case section + :fonts (dom/set-html-title (tr "title.dashboard.fonts" tname)) + :providers (dom/set-html-title (tr "title.dashboard.font-providers" tname))))))) + +(defn- bad-font-family-tmp? + [font] + (and (contains? font :font-family-tmp) + (str/blank? (:font-family-tmp font)))) (mf/defc header - {::mf/wrap [mf/memo]} - [{:keys [section team] :as props}] - (use-set-page-title team section) + {::mf/props :obj + ::mf/memo true + ::mf/private true} + [{:keys [section team]}] + (use-page-title team section) [:header {:class (stl/css :dashboard-header)} [:div#dashboard-fonts-title {:class (stl/css :dashboard-title)} [:h1 (tr "labels.fonts")]]]) (mf/defc font-variant-display-name + {::mf/props :obj + ::mf/private true} [{:keys [variant]}] [:* [:span (cm/font-weight->name (:font-weight variant))] (when (not= "normal" (:font-style variant)) [:span " " (str/capital (:font-style variant))])]) -(mf/defc fonts-upload +(mf/defc uploaded-fonts + {::mf/props :obj + ::mf/private true} [{:keys [team installed-fonts] :as props}] - (let [fonts* (mf/use-state {}) - fonts (deref fonts*) - input-ref (mf/use-ref) - uploading (mf/use-state #{}) + (let [fonts* (mf/use-state {}) + fonts (deref fonts*) + font-vals (mf/with-memo [fonts] + (->> fonts + (into [] (map val)) + (not-empty))) - bad-font-family-tmp? - (mf/use-fn - (fn [font] - (and (contains? font :font-family-tmp) - (str/blank? (:font-family-tmp font))))) + team-id (:id team) - disable-upload-all? (some bad-font-family-tmp? (vals fonts)) + input-ref (mf/use-ref) - handle-click + uploading* (mf/use-state #{}) + uploading (deref uploading*) + + disable-upload-all? + (some bad-font-family-tmp? fonts) + + problematic-fonts? + (some :height-warning? (vals fonts)) + + on-click (mf/use-fn #(dom/click (mf/ref-val input-ref))) - handle-selected + on-selected (mf/use-fn - (mf/deps team installed-fonts) + (mf/deps team-id installed-fonts) (fn [blobs] - (->> (df/process-upload blobs (:id team)) + (->> (df/process-upload blobs team-id) (rx/subs! (fn [result] (swap! fonts* df/merge-and-group-fonts installed-fonts result)) (fn [error] (js/console.error "error" error)))))) - on-upload + on-upload* (mf/use-fn - (mf/deps team) - (fn [item] - (swap! uploading conj (:id item)) + (fn [{:keys [id] :as item}] + (swap! uploading* conj id) (->> (rp/cmd! :create-font-variant item) (rx/delay-at-least 2000) (rx/subs! (fn [font] - (swap! fonts* dissoc (:id item)) - (swap! uploading disj (:id item)) + (swap! fonts* dissoc id) + (swap! uploading* disj id) (st/emit! (df/add-font font))) (fn [error] (js/console.log "error" error)))))) - on-upload-all - (fn [items] - (run! on-upload items)) + on-upload + (mf/use-fn + (mf/deps fonts on-upload*) + (fn [event] + (let [id (-> (dom/get-current-target event) + (dom/get-data "id") + (parse-uuid)) + item (get fonts id)] + (on-upload* item)))) on-blur-name - (fn [id event] - (let [name (dom/get-target-val event)] - (when-not (str/blank? name) - (swap! fonts* df/rename-and-regroup id name installed-fonts)))) + (mf/use-fn + (mf/deps installed-fonts) + (fn [event] + (let [target (dom/get-current-target event) + id (-> target + (dom/get-data "id") + (parse-uuid)) + name (dom/get-value target)] + (when-not (str/blank? name) + (swap! fonts* df/rename-and-regroup id name installed-fonts))))) on-change-name - (fn [id event] - (let [name (dom/get-target-val event)] - (swap! fonts* update-in [id] #(assoc % :font-family-tmp name)))) + (mf/use-fn + (fn [event] + (let [target (dom/get-current-target event) + id (-> target + (dom/get-data "id") + (parse-uuid)) + name (dom/get-value target)] + (swap! fonts* update id assoc :font-family-tmp name)))) on-delete (mf/use-fn (mf/deps team) - (fn [{:keys [id] :as item}] - (swap! fonts* dissoc id))) + (fn [event] + (let [id (-> (dom/get-current-target event) + (dom/get-data "id") + (parse-uuid))] + (swap! fonts* dissoc id)))) - on-dismiss-all - (fn [items] - (run! on-delete items)) + on-upload-all + (mf/use-fn + (mf/deps font-vals) + (fn [_] + (run! on-upload* font-vals))) - problematic-fonts? (some :height-warning? (vals fonts)) - - handle-upload-all - (mf/use-fn (mf/deps fonts) #(on-upload-all (vals fonts))) - - handle-dismiss-all - (mf/use-fn (mf/deps fonts) #(on-dismiss-all (vals fonts)))] + on-dismis-all + (mf/use-fn + (mf/deps fonts) + (fn [_] + (run! on-delete (vals fonts))))] [:div {:class (stl/css :dashboard-fonts-upload)} [:div {:class (stl/css :dashboard-fonts-hero)} @@ -135,14 +170,14 @@ [:& i18n/tr-html {:label "dashboard.fonts.hero-text1"}] [:button {:class (stl/css :btn-primary) - :on-click handle-click + :on-click on-click :tab-index "0"} [:span (tr "labels.add-custom-font")] [:& file-uploader {:input-id "font-upload" :accept cm/str-font-types :multi true :ref input-ref - :on-selected handle-selected}]] + :on-selected on-selected}]] [:& context-notification {:content (tr "dashboard.fonts.hero-text2") :type :default @@ -154,31 +189,32 @@ :is-html true}])]] [:* - (when (some? (vals fonts)) + (when (seq fonts) [:div {:class (stl/css :font-item :table-row)} - [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count (vals fonts))))] + [:span (tr "dashboard.fonts.fonts-added" (i18n/c (count fonts)))] [:div {:class (stl/css :table-field :options)} - [:button {:class (stl/css-case :btn-primary true - :disabled disable-upload-all?) - :on-click handle-upload-all + [:button {:class (stl/css-case + :btn-primary true + :disabled disable-upload-all?) + :on-click on-upload-all :data-test "upload-all" :disabled disable-upload-all?} [:span (tr "dashboard.fonts.upload-all")]] [:button {:class (stl/css :btn-secondary) - :on-click handle-dismiss-all + :on-click on-dismis-all :data-test "dismiss-all"} [:span (tr "dashboard.fonts.dismiss-all")]]]]) - (for [item (sort-by :font-family (vals fonts))] - (let [uploading? (contains? @uploading (:id item)) - disable-upload? (or uploading? - (bad-font-family-tmp? item))] + (for [{:keys [id] :as item} (sort-by :font-family font-vals)] + (let [uploading? (contains? uploading id) + disable-upload? (or uploading? (bad-font-family-tmp? item))] [:div {:class (stl/css :font-item :table-row) - :key (:id item)} + :key (dm/str id)} [:div {:class (stl/css :table-field :family)} [:input {:type "text" - :on-blur #(on-blur-name (:id item) %) - :on-change #(on-change-name (:id item) %) + :data-id (dm/str id) + :on-blur on-blur-name + :on-change on-change-name :default-value (:font-family item)}]] [:div {:class (stl/css :table-field :variants)} [:span {:class (stl/css :label)} @@ -190,115 +226,151 @@ [:div {:class (stl/css :table-field :options)} (when (:height-warning? item) - [:span {:class (stl/css :icon :failure)} i/msg-neutral-refactor]) + [:span {:class (stl/css :icon :failure)} + i/msg-neutral-refactor]) - [:button {:on-click #(on-upload item) - :class (stl/css-case :btn-primary true - :upload-button true - :disabled disable-upload?) + [:button {:on-click on-upload + :data-id (dm/str id) + :class (stl/css-case + :btn-primary true + :upload-button true + :disabled disable-upload?) :disabled disable-upload?} - (if uploading? + (if ^boolean uploading? (tr "labels.uploading") (tr "labels.upload"))] [:span {:class (stl/css :icon :close) - :on-click #(on-delete item)} i/close-refactor]]]))]])) + :data-id (dm/str id) + :on-click on-delete} + i/close-refactor]]]))]])) + +(mf/defc installed-font-context-menu + {::mf/props :obj + ::mf/private true} + [{:keys [is-open on-close on-edit on-delete]}] + (let [options (mf/with-memo [on-edit on-delete] + [{:option-name (tr "labels.edit") + :id "font-edit" + :option-handler on-edit} + {:option-name (tr "labels.delete") + :id "font-delete" + :option-handler on-delete}])] + [:& context-menu-a11y + {:on-close on-close + :show is-open + :fixed? false + :min-width? true + :top -15 + :left -115 + :options options + :workspace? false}])) (mf/defc installed-font - [{:keys [font-id variants] :as props}] + {::mf/props :obj + ::mf/private true + ::mf/memo true} + [{:keys [font-id variants]}] (let [font (first variants) - variants (sort-by (fn [item] - [(:font-weight item) - (if (= "normal" (:font-style item)) 1 2)]) - variants) + menu-open* (mf/use-state false) + menu-open? (deref menu-open*) + edition* (mf/use-state false) + edition? (deref edition*) - open-menu? (mf/use-state false) - edit? (mf/use-state false) state* (mf/use-state (:font-family font)) font-family (deref state*) + variants + (mf/with-memo [variants] + (sort-by (fn [item] + [(:font-weight item) + (if (= "normal" (:font-style item)) 1 2)]) + variants)) on-change - (mf/use-callback + (mf/use-fn (fn [event] (reset! state* (dom/get-target-val event)))) + on-edit + (mf/use-fn #(reset! edition* true)) + + on-menu-open + (mf/use-fn #(reset! menu-open* true)) + + on-menu-close + (mf/use-fn #(reset! menu-open* false)) + on-save - (mf/use-callback + (mf/use-fn (mf/deps font-family) (fn [_] + (reset! edition* false) (when-not (str/blank? font-family) - (st/emit! (df/update-font {:id font-id :name font-family}))) - (reset! edit? false))) + (st/emit! (df/update-font {:id font-id :name font-family}))))) on-key-down - (mf/use-callback + (mf/use-fn (mf/deps on-save) (fn [event] (when (kbd/enter? event) (on-save event)))) on-cancel - (mf/use-callback + (mf/use-fn (fn [_] - (reset! edit? false) + (reset! edition* false) (reset! state* (:font-family font)))) - delete-font-fn - (mf/use-callback + on-delete-font + (mf/use-fn (mf/deps font-id) (fn [] - (st/emit! (df/delete-font font-id)))) - - delete-variant-fn - (mf/use-callback - (fn [id] - (st/emit! (df/delete-font-variant id)))) - - on-delete - (mf/use-callback - (mf/deps delete-font-fn) - (fn [] - (st/emit! (modal/show - {:type :confirm - :title (tr "modals.delete-font.title") - :message (tr "modals.delete-font.message") - :accept-label (tr "labels.delete") - :on-accept (fn [_props] (delete-font-fn))})))) + (let [options {:type :confirm + :title (tr "modals.delete-font.title") + :message (tr "modals.delete-font.message") + :accept-label (tr "labels.delete") + :on-accept (fn [_props] + (st/emit! (df/delete-font font-id)))}] + (st/emit! (modal/show options))))) on-delete-variant - (mf/use-callback - (mf/deps delete-variant-fn) - (fn [id] - (st/emit! (modal/show - {:type :confirm - :title (tr "modals.delete-font-variant.title") - :message (tr "modals.delete-font-variant.message") - :accept-label (tr "labels.delete") - :on-accept (fn [_props] - (delete-variant-fn id))}))))] + (mf/use-fn + (fn [event] + (let [id (-> (dom/get-current-target event) + (dom/get-data "id") + (parse-uuid)) + options {:type :confirm + :title (tr "modals.delete-font-variant.title") + :message (tr "modals.delete-font-variant.message") + :accept-label (tr "labels.delete") + :on-accept (fn [_props] + (st/emit! (df/delete-font-variant id)))}] + (st/emit! (modal/show options)))))] [:div {:class (stl/css :font-item :table-row)} [:div {:class (stl/css :table-field :family)} - (if @edit? + (if ^boolean edition? [:input {:type "text" + :auto-focus true :default-value font-family :on-key-down on-key-down :on-change on-change}] [:span (:font-family font)])] [:div {:class (stl/css :table-field :variants)} - (for [item variants] + (for [{:keys [id] :as item} variants] [:div {:class (stl/css :variant) - :key (dm/str (:id item) "-variant")} + :key (dm/str id)} [:span {:class (stl/css :label)} [:& font-variant-display-name {:variant item}]] [:span {:class (stl/css :icon :close) - :on-click #(on-delete-variant (:id item))} + :data-id (dm/str id) + :on-click on-delete-variant} i/add-refactor]])] - (if @edit? + (if ^boolean edition? [:div {:class (stl/css :table-field :options)} [:button {:disabled (str/blank? font-family) @@ -307,27 +379,19 @@ :btn-disabled (str/blank? font-family))} (tr "labels.save")] [:button {:class (stl/css :icon :close) - :on-click on-cancel} i/close-refactor]] + :on-click on-cancel} + i/close-refactor]] [:div {:class (stl/css :table-field :options)} [:span {:class (stl/css :icon) - :on-click #(reset! open-menu? true)} + :on-click on-menu-open} i/menu-refactor] - [:& context-menu-a11y {:on-close #(reset! open-menu? false) - :show @open-menu? - :fixed? false - :min-width? true - :top -15 - :left -115 - :options [{:option-name (tr "labels.edit") - :id "font-edit" - :option-handler #(reset! edit? true)} - {:option-name (tr "labels.delete") - :id "font-delete" - :option-handler on-delete}] - :workspace? false}]])])) - + [:& installed-font-context-menu + {:on-close on-menu-close + :is-open menu-open? + :on-delete on-delete-font + :on-edit on-edit}]])])) (mf/defc installed-fonts [{:keys [fonts] :as props}] @@ -377,7 +441,7 @@ [:* [:& header {:team team :section :fonts}] [:section {:class (stl/css :dashboard-container :dashboard-fonts)} - [:& fonts-upload {:team team :installed-fonts fonts}] + [:& uploaded-fonts {:team team :installed-fonts fonts}] [:& installed-fonts {:team team :fonts fonts}]]])) (mf/defc font-providers-page