diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index a20913f925..865de22e7e 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -88,6 +88,7 @@ --button-constraint-border-color-rest: var(--color-background-tertiary); --button-constraint-border-color-hover: var(--color-accent-primary-muted); --button-constraint-background-color-hover: var(--color-accent-primary); + --constraint-widget-background-color: var(--color-background-tertiary); --constraint-center-area-background-color: var(--color-background-primary); @@ -138,6 +139,8 @@ // ICONS --icon-foreground: var(--color-foreground-secondary); --icon-foreground-hover: var(--color-foreground-primary); + --icon-foreground-accept: var(--ok-color); + --icon-foreground-discard: var(--error-color); // INPUTS, SELECTS, DROPDOWNS --input-background-color: var(--color-background-tertiary); diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 99a078fbb6..c2a624a2ad 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -811,12 +811,7 @@ (ptk/reify ::update-component-thumbnail ptk/WatchEvent (watch [_ state _] - (rx/of (update-component-thumbnail-sync state component-id file-id "component")) - #_(let [data (get state :workspace-data) - component (ctkl/get-component data component-id) - page-id (:main-instance-page component) - root-id (:main-instance-id component)] - (rx/of (dwt/request-thumbnail file-id page-id root-id "component")))))) + (rx/of (update-component-thumbnail-sync state component-id file-id "component"))))) (defn- find-shape-index [objects id shape-id] diff --git a/frontend/src/app/main/data/workspace/thumbnails.cljs b/frontend/src/app/main/data/workspace/thumbnails.cljs index 5770542314..6582a01c88 100644 --- a/frontend/src/app/main/data/workspace/thumbnails.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails.cljs @@ -10,7 +10,6 @@ [app.common.files.helpers :as cfh] [app.common.logging :as l] [app.common.thumbnails :as thc] - [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] [app.main.data.workspace.notifications :as-alias wnt] [app.main.data.workspace.state-helpers :as wsh] @@ -183,7 +182,7 @@ (rx/filter (ptk/type? ::clear-thumbnail)) (rx/filter #(= (deref %) object-id))))))))) -(defn- extract-frame-changes +(defn- extract-root-frame-changes "Process a changes set in a commit to extract the frames that are changing" [page-id [event [old-data new-data]]] (let [changes (-> event deref :changes) @@ -209,10 +208,10 @@ new-frame-id (if (cfh/frame-shape? new-shape) id (:frame-id new-shape))] (cond-> #{} - (and (some? old-frame-id) (not= uuid/zero old-frame-id)) + (cfh/root-frame? old-objects old-frame-id) (conj old-frame-id) - (and (some? new-frame-id) (not= uuid/zero new-frame-id)) + (cfh/root-frame? new-objects new-frame-id) (conj new-frame-id))))] (into #{} @@ -251,16 +250,16 @@ (rx/filter dch/commit-changes?) (rx/observe-on :async) (rx/with-latest-from workspace-data-s) - (rx/merge-map (partial extract-frame-changes page-id)) - (rx/tap #(l/trc :hint "inconming change" :origin "local" :frame-id (dm/str %)))) + (rx/merge-map (partial extract-root-frame-changes page-id)) + (rx/tap #(l/trc :hint "incoming change" :origin "local" :frame-id (dm/str %)))) ;; NOTIFICATIONS CHANGES (->> stream (rx/filter (ptk/type? ::wnt/handle-file-change)) (rx/observe-on :async) (rx/with-latest-from workspace-data-s) - (rx/merge-map (partial extract-frame-changes page-id)) - (rx/tap #(l/trc :hint "inconming change" :origin "notifications" :frame-id (dm/str %)))) + (rx/merge-map (partial extract-root-frame-changes page-id)) + (rx/tap #(l/trc :hint "incoming change" :origin "notifications" :frame-id (dm/str %)))) ;; PERSISTENCE CHANGES (->> stream @@ -270,7 +269,7 @@ (and (= file-id file-id) (= page-id page-id)))) (rx/map (fn [[_ _ frame-id]] frame-id)) - (rx/tap #(l/trc :hint "inconming change" :origin "persistence" :frame-id (dm/str %))))) + (rx/tap #(l/trc :hint "incoming change" :origin "persistence" :frame-id (dm/str %))))) (rx/share)) @@ -285,15 +284,13 @@ ;; and interrupt any ongoing update-thumbnail process ;; related to current frame-id (->> changes-s - (rx/map (fn [frame-id] - (clear-thumbnail file-id page-id frame-id "frame")))) + (rx/map #(clear-thumbnail file-id page-id % "frame"))) ;; Generate thumbnails in batchs, once user becomes ;; inactive for some instant (->> changes-s (rx/buffer-until notifier-s) (rx/mapcat #(into #{} %)) - (rx/map (fn [frame-id] - (request-thumbnail file-id page-id frame-id "frame"))))) + (rx/map #(request-thumbnail file-id page-id % "frame")))) (rx/take-until stopper-s)))))) diff --git a/frontend/src/app/main/ui/dashboard/fonts.cljs b/frontend/src/app/main/ui/dashboard/fonts.cljs index 26519781f6..c51ca93458 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.cljs +++ b/frontend/src/app/main/ui/dashboard/fonts.cljs @@ -7,13 +7,15 @@ (ns app.main.ui.dashboard.fonts (:require-macros [app.main.style :as stl]) (:require + [app.common.data.macros :as dm] [app.common.media :as cm] [app.main.data.fonts :as df] [app.main.data.modal :as modal] [app.main.refs :as refs] [app.main.repo :as rp] [app.main.store :as st] - [app.main.ui.components.context-menu :refer [context-menu]] + + [app.main.ui.components.context-menu-a11y :refer [context-menu-a11y]] [app.main.ui.components.file-uploader :refer [file-uploader]] [app.main.ui.icons :as i] [app.util.dom :as dom] @@ -53,33 +55,34 @@ (mf/defc fonts-upload [{:keys [team installed-fonts] :as props}] - (let [fonts (mf/use-state {}) + (let [fonts* (mf/use-state {}) + fonts (deref fonts*) input-ref (mf/use-ref) uploading (mf/use-state #{}) - on-click - (mf/use-callback #(dom/click (mf/ref-val input-ref))) + handle-click + (mf/use-fn #(dom/click (mf/ref-val input-ref))) - on-selected - (mf/use-callback + handle-selected + (mf/use-fn (mf/deps team installed-fonts) (fn [blobs] (->> (df/process-upload blobs (:id team)) (rx/subs! (fn [result] - (swap! fonts df/merge-and-group-fonts installed-fonts result)) + (swap! fonts* df/merge-and-group-fonts installed-fonts result)) (fn [error] (js/console.error "error" error)))))) on-upload - (mf/use-callback + (mf/use-fn (mf/deps team) (fn [item] (swap! uploading conj (:id item)) (->> (rp/cmd! :create-font-variant item) (rx/delay-at-least 2000) (rx/subs! (fn [font] - (swap! fonts dissoc (:id item)) + (swap! fonts* dissoc (:id item)) (swap! uploading disj (:id item)) (st/emit! (df/add-font font))) (fn [error] @@ -92,25 +95,25 @@ on-blur-name (fn [id event] (let [name (dom/get-target-val event)] - (swap! fonts df/rename-and-regroup id name installed-fonts))) + (swap! fonts* df/rename-and-regroup id name installed-fonts))) on-delete - (mf/use-callback + (mf/use-fn (mf/deps team) (fn [{:keys [id] :as item}] - (swap! fonts dissoc id))) + (swap! fonts* dissoc id))) on-dismiss-all (fn [items] (run! on-delete items)) - problematic-fonts? (some :height-warning? (vals @fonts)) + problematic-fonts? (some :height-warning? (vals fonts)) handle-upload-all - (mf/use-callback (mf/deps @fonts) #(on-upload-all (vals @fonts))) + (mf/use-fn (mf/deps fonts) #(on-upload-all (vals fonts))) handle-dismiss-all - (mf/use-callback (mf/deps @fonts) #(on-dismiss-all (vals @fonts)))] + (mf/use-fn (mf/deps fonts) #(on-dismiss-all (vals fonts)))] [:div {:class (stl/css :dashboard-fonts-upload)} [:div {:class (stl/css :dashboard-fonts-hero)} @@ -118,45 +121,47 @@ [:h2 (tr "labels.upload-custom-fonts")] [:& i18n/tr-html {:label "dashboard.fonts.hero-text1"}] - [:button - {:class (stl/css :btn-primary) - :on-click on-click - :tab-index "0"} + [:button {:class (stl/css :btn-primary) + :on-click handle-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 on-selected}]] + :on-selected handle-selected}]] [:div {:class (stl/css :banner)} - [:div {:class (stl/css :icon)} i/msg-info] + [:div {:class (stl/css :icon)} i/msg-neutral-refactor] [:div {:class (stl/css :content)} [:& i18n/tr-html {:tag-name "span" :label "dashboard.fonts.hero-text2"}]]] (when problematic-fonts? [:div {:class (stl/css :banner :warning)} - [:div {:class (stl/css :icon)} i/msg-warning] + [:div {:class (stl/css :icon)} i/msg-warning-refactor] [:div {:class (stl/css :content)} [:& i18n/tr-html {:tag-name "span" :label "dashboard.fonts.warning-text"}]]])]] [:* - (when (some? (vals @fonts)) + (when (some? (vals 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 (vals fonts))))] [:div {:class (stl/css :table-field :options)} [:button {:class (stl/css :btn-primary) - :on-click handle-upload-all :data-test "upload-all"} + :on-click handle-upload-all + :data-test "upload-all"} [:span (tr "dashboard.fonts.upload-all")]] [:button {:class (stl/css :btn-secondary) - :on-click handle-dismiss-all :data-test "dismiss-all"} + :on-click handle-dismiss-all + :data-test "dismiss-all"} [:span (tr "dashboard.fonts.dismiss-all")]]]]) - (for [item (sort-by :font-family (vals @fonts))] + (for [item (sort-by :font-family (vals fonts))] (let [uploading? (contains? @uploading (:id item))] - [:div {:class (stl/css :font-item :table-row) :key (:id item)} + [:div {:class (stl/css :font-item :table-row) + :key (:id item)} [:div {:class (stl/css :table-field :family)} [:input {:type "text" :on-blur #(on-blur-name (:id item) %) @@ -167,44 +172,45 @@ [:div {:class (stl/css :table-field :filenames)} (for [item (:names item)] - [:span item])] + [:span {:key (dm/str "name-" item)} item])] [:div {:class (stl/css :table-field :options)} (when (:height-warning? item) - [:span {:class (stl/css :icon :failure)} i/msg-warning]) + [:span {:class (stl/css :icon :failure)} i/msg-warning-refactor]) - [:button - {:on-click #(on-upload item) - :class (stl/css-case :btn-primary true - :upload-button true - :disabled uploading?) - :disabled uploading?} + [:button {:on-click #(on-upload item) + :class (stl/css-case :btn-primary true + :upload-button true + :disabled uploading?) + :disabled uploading?} (if uploading? (tr "labels.uploading") (tr "labels.upload"))] [:span {:class (stl/css :icon :close) - :on-click #(on-delete item)} i/close]]]))]])) + :on-click #(on-delete item)} i/close-refactor]]]))]])) (mf/defc installed-font [{:keys [font-id variants] :as props}] - (let [font (first variants) + (let [font (first variants) - variants (sort-by (fn [item] - [(:font-weight item) - (if (= "normal" (:font-style item)) 1 2)]) - variants) + variants (sort-by (fn [item] + [(:font-weight item) + (if (= "normal" (:font-style item)) 1 2)]) + variants) + + open-menu? (mf/use-state false) + edit? (mf/use-state false) + state* (mf/use-var (:font-family font)) + font-family (deref state*) - open-menu? (mf/use-state false) - edit? (mf/use-state false) - state (mf/use-var (:font-family font)) on-change (fn [event] - (reset! state (dom/get-target-val event))) + (reset! state* (dom/get-target-val event))) on-save (fn [_] - (let [font-family @state] + (let [font-family font-family] (when-not (str/blank? font-family) (st/emit! (df/update-font {:id font-id @@ -219,7 +225,7 @@ on-cancel (fn [_] (reset! edit? false) - (reset! state (:font-family font))) + (reset! state* (:font-family font))) delete-font-fn (fn [] (st/emit! (df/delete-font font-id))) @@ -250,43 +256,51 @@ [:div {:class (stl/css :table-field :family)} (if @edit? [:input {:type "text" - :default-value @state + :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] - [:div {:class (stl/css :variant)} + [:div {:class (stl/css :variant) + :key (dm/str (:id item) "-variant")} [:span {:class (stl/css :label)} [:& font-variant-display-name {:variant item}]] [:span {:class (stl/css :icon :close) :on-click #(on-delete-variant (:id item))} - i/plus]])] + i/add-refactor]])] (if @edit? [:div {:class (stl/css :table-field :options)} [:button - {:disabled (str/blank? @state) + {:disabled (str/blank? font-family) :on-click on-save :class (stl/css-case :btn-primary true - :btn-disabled (str/blank? @state))} + :btn-disabled (str/blank? font-family))} (tr "labels.save")] [:button {:class (stl/css :icon :close) - :on-click on-cancel} i/close]] + :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)} i/actions] - [:& context-menu - {:on-close #(reset! open-menu? false) - :show @open-menu? - :fixed? false - :top -15 - :left -115 - :options [[(tr "labels.edit") #(reset! edit? true) nil "font-edit"] - [(tr "labels.delete") on-delete nil "font-delete"]]}]])])) + :on-click #(reset! open-menu? true)} + 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}]])])) (mf/defc installed-fonts @@ -297,7 +311,7 @@ #(str/includes? (str/lower (:font-family %)) @sterm) on-change - (mf/use-callback + (mf/use-fn (fn [event] (let [val (dom/get-target-val event)] (reset! sterm (str/lower val)))))] @@ -317,7 +331,7 @@ (for [[font-id variants] (->> (vals fonts) (filter matches?) (group-by :font-id))] - [:& installed-font {:key (str font-id) + [:& installed-font {:key (dm/str font-id "-installed") :font-id font-id :variants variants}]) @@ -328,7 +342,7 @@ :else [:div {:class (stl/css :fonts-placeholder)} - [:div {:class (stl/css :icon)} i/text] + [:div {:class (stl/css :icon)} i/text-refactor] [:div {:class (stl/css :label)} (tr "dashboard.fonts.empty-placeholder")]])])) (mf/defc fonts-page diff --git a/frontend/src/app/main/ui/dashboard/fonts.scss b/frontend/src/app/main/ui/dashboard/fonts.scss index 8741a6289f..d53e3b5ec1 100644 --- a/frontend/src/app/main/ui/dashboard/fonts.scss +++ b/frontend/src/app/main/ui/dashboard/fonts.scss @@ -17,6 +17,8 @@ .btn-primary { font-size: $fs-11; + height: $s-32; + min-width: $s-100; } } @@ -150,7 +152,7 @@ &:hover { .icon svg { - fill: $df-secondary; + stroke: $df-secondary; } } } @@ -187,21 +189,20 @@ &.failure { margin-right: $s-12; svg { - fill: var(--warning-color); + stroke: var(--warning-color); } } svg { width: $s-16; height: $s-16; - fill: $df-secondary; + stroke: $df-secondary; } &.close { background: none; border: none; svg { - transform: rotate(45deg); - fill: $df-secondary; + stroke: $df-secondary; } } } @@ -273,7 +274,7 @@ padding-top: $s-12; svg { - fill: $df-secondary; + stroke: $df-secondary; height: $s-20; width: $s-20; } @@ -287,7 +288,7 @@ &.warning { background-color: $db-cuaternary; .icon svg { - fill: var(--warning-color); + stroke: var(--warning-color); } } } @@ -310,7 +311,7 @@ width: 100%; .icon svg { - fill: $df-secondary; + stroke: $df-secondary; width: $s-32; height: $s-32; } diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index 90243b9178..68f19b9cf8 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -324,14 +324,14 @@ (mf/use-fn (fn [event] (let [origin (dom/get-target event) - over-section? (dom/class? origin "viewer-section") + over-section? (dom/get-data origin "viewer-section") layout (dom/get-element "viewer-layout") - has-force? (dom/class? layout "force-visible")] + has-force? (dom/get-data layout "force-visible")] (when over-section? - (if has-force? - (dom/remove-class! layout "force-visible") - (dom/add-class! layout "force-visible")))))) + (if (= has-force? "true") + (dom/set-data! layout "force-visible" false) + (dom/set-data! layout "force-visible" true)))))) on-click (mf/use-fn @@ -351,10 +351,11 @@ (mf/use-fn (fn [event] (let [event (.getBrowserEvent ^js event) - wrapper (dom/get-element-by-class "inspect-svg-wrapper") - section (dom/get-element-by-class "inspect-svg-container") + wrapper (dom/get-element "inspect-svg-wrapper") + section (dom/get-element "inspect-svg-container") target (.-target event)] - (when (or (dom/child? target wrapper) (dom/class? target "inspect-svg-container")) + ;; TODO: Reemplazar el dom/class? por un data-attribute + (when (or (dom/child? target wrapper) (dom/id? target "inspect-svg-container")) (let [norm-event ^js (nw/normalize-wheel event) mod? (kbd/mod? event) shift? (kbd/shift? event) @@ -516,8 +517,9 @@ {:class (stl/css-case :force-visible (:show-thumbnails local) :viewer-layout (not= section :inspect) - :inspect-layout (= section :inspect) - :fullscreen fullscreen?)} + :inspect-layout (= section :inspect)) + :data-fullscreen fullscreen? + :data-force-visible (:show-thumbnails local)} [:div {:class (stl/css :viewer-content)} [:& header/header {:project project @@ -542,6 +544,7 @@ [:section {:id "viewer-section" :ref viewer-section-ref + :data-viewer-section true :class (stl/css-case :viewer-section true :fulscreen fullscreen?) :on-click click-on-screen} diff --git a/frontend/src/app/main/ui/viewer.scss b/frontend/src/app/main/ui/viewer.scss index e31ff3b289..897cf905af 100644 --- a/frontend/src/app/main/ui/viewer.scss +++ b/frontend/src/app/main/ui/viewer.scss @@ -101,7 +101,7 @@ width: 100%; height: $s-40; padding-right: 0 $s-8 $s-40 $s-8; - transition: bottom 400ms ease 300ms; + transition: transform 400ms ease 300ms; z-index: $z-index-2; } @@ -178,3 +178,12 @@ :global(svg#loader-pencil) { fill: var(--icon-foreground); } + +/** FULLSCREEN */ +[data-fullscreen="true"] .viewer-bottom { + transform: translateY($s-40); +} + +[data-force-visible="true"] .viewer-bottom { + transform: translateY(0); +} diff --git a/frontend/src/app/main/ui/viewer/header.cljs b/frontend/src/app/main/ui/viewer/header.cljs index b168cad56d..cfa7324889 100644 --- a/frontend/src/app/main/ui/viewer/header.cljs +++ b/frontend/src/app/main/ui/viewer/header.cljs @@ -283,7 +283,8 @@ (st/emit! (dv/go-to-section section)) (open-login-dialog)))))] - [:header {:class (stl/css :viewer-header)} + [:header {:class (stl/css-case :viewer-header true + :fullscreen (mf/deref fullscreen-ref))} [:div {:class (stl/css :nav-zone)} ;; If the user doesn't have permission we disable the link [:a {:class (stl/css :home-link) diff --git a/frontend/src/app/main/ui/viewer/header.scss b/frontend/src/app/main/ui/viewer/header.scss index 633d7bf9d8..6eebe53ba5 100644 --- a/frontend/src/app/main/ui/viewer/header.scss +++ b/frontend/src/app/main/ui/viewer/header.scss @@ -18,6 +18,7 @@ height: $s-48; width: 100vw; padding: $s-8 $s-12; + transition: transform 400ms ease 300ms; background-color: var(--panel-background-color); } @@ -300,3 +301,12 @@ } } } + +/** FULLSCREEN */ +[data-fullscreen="true"] .viewer-header { + transform: translateY(-$s-48); +} + +[data-force-visible="true"] .viewer-header { + transform: translateY(0); +} diff --git a/frontend/src/app/main/ui/viewer/inspect.cljs b/frontend/src/app/main/ui/viewer/inspect.cljs index b2397d30b9..110317c186 100644 --- a/frontend/src/app/main/ui/viewer/inspect.cljs +++ b/frontend/src/app/main/ui/viewer/inspect.cljs @@ -94,12 +94,12 @@ [:& left-sidebar {:frame frame :local local :page page}] - [:div {:class (stl/css :inspect-svg-wrapper) - :data-value (pr-str (:id frame)) - :on-click handle-select-frame} + [:div#inspect-svg-wrapper {:class (stl/css :inspect-svg-wrapper) + :data-value (pr-str (:id frame)) + :on-click handle-select-frame} [:& viewer-pagination {:index index :num-frames (count (:frames page)) :left-bar true :right-bar true}] - [:div {:class (stl/css :inspect-svg-container) - :ref inspect-svg-container-ref} + [:div#inspect-svg-container {:class (stl/css :inspect-svg-container) + :ref inspect-svg-container-ref} [:& render-frame-svg {:frame frame :page page :local local :size size}]]] [:div {:class (stl/css-case :sidebar-container true diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 11bf957743..2dc7e32890 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -118,12 +118,12 @@ tries-ref (mf/use-ref 0) imposter-ref (mf/use-ref nil) - imposter-loaded-ref (mf/use-ref false) + imposter-loaded (mf/use-state false) task-ref (mf/use-ref nil) on-load (mf/use-fn (fn [] (mf/set-ref-val! tries-ref 0) - (mf/set-ref-val! imposter-loaded-ref true))) + (reset! imposter-loaded true))) on-error (mf/use-fn (fn [] (let [current-tries (mf/ref-val tries-ref) @@ -158,7 +158,7 @@ :opacity (when ^boolean hidden? 0)} ;; When there is no thumbnail, we generate a empty rect. - (when (and (not ^boolean content-visible?) (not (mf/ref-val imposter-loaded-ref))) + (when (and (not ^boolean content-visible?) (not @imposter-loaded)) [:g.frame-placeholder [:rect {:x x :y y diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs index bcc63e5ed5..2dbc35ab5d 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.cljs @@ -106,46 +106,66 @@ (fn [] (st/emit! (dw/set-annotations-id-for-create nil))))) ;; cleanup set-annotationsid-for-create on unload (when (or creating? annotation) - [:div.component-annotation {:class (dom/classnames :editing @editing? :creating creating?)} - [:div.title {:class (dom/classnames :expandeable (not (or @editing? creating?))) - :on-click #(expand (not annotations-expanded?))} - [:div (if (or @editing? creating?) - (if @editing? - (tr "workspace.options.component.edit-annotation") - (tr "workspace.options.component.create-annotation")) - [:* (if annotations-expanded? - [:div.expand i/arrow-down] - [:div.expand i/arrow-slide]) - (tr "workspace.options.component.annotation")])] - [:div + [:div {:class (stl/css-case :component-annotation true + :editing @editing? + :creating creating?)} + [:div {:class (stl/css-case :annotation-title true + :expandeable (not (or @editing? creating?)) + :expanded annotations-expanded?) + :on-click #(expand (not annotations-expanded?))} + + (if (or @editing? creating?) + [:span {:class (stl/css :annotation-text)} + (if @editing? + (tr "workspace.options.component.edit-annotation") + (tr "workspace.options.component.create-annotation"))] + + [:* + [:span {:class (stl/css-case :icon-arrow true + :expanded annotations-expanded?)} + i/arrow-refactor] + [:span {:class (stl/css :annotation-text)} + (tr "workspace.options.component.annotation")]]) + + [:div {:class (stl/css :icons-wrapper)} (when (and main-instance? annotations-expanded?) (if (or @editing? creating?) [:* - [:div.icon {:title (if creating? (tr "labels.create") (tr "labels.save")) - :on-click save - :class (dom/classnames :hidden @invalid-text?)} i/tick] - [:div.icon {:title (tr "labels.discard") - :on-click discard} i/cross]] - [:* - [:div.icon {:title (tr "labels.edit") - :on-click edit} i/pencil] - [:div.icon {:title (tr "labels.delete") - :on-click on-delete-annotation} i/trash]]))]] + [:div {:title (if creating? (tr "labels.create") (tr "labels.save")) + :on-click save + :class (stl/css-case :icon true + :icon-tick true + :hidden @invalid-text?)} + i/tick-refactor] + [:div {:class (stl/css :icon :icon-cross) + :title (tr "labels.discard") + :on-click discard} + i/close-refactor]] - [:div {:class (dom/classnames :hidden (not annotations-expanded?))} - [:div.grow-wrap - [:div.texarea-copy] + [:* + [:div {:class (stl/css :icon :icon-edit) + :title (tr "labels.edit") + :on-click edit} + i/curve-refactor] + [:div {:class (stl/css :icon :icon-trash) + :title (tr "labels.delete") + :on-click on-delete-annotation} + i/delete-refactor]]))]] + + [:div {:class (stl/css-case :hidden (not annotations-expanded?))} + [:div {:class (stl/css :grow-wrap)} + [:div {:class (stl/css :texarea-copy)}] [:textarea {:ref textarea-ref :id "annotation-textarea" :data-debug annotation - :auto-focus true + :auto-focus (or @editing? creating?) :maxLength 300 :on-input autogrow :default-value annotation :read-only (not (or creating? @editing?))}]] (when (or @editing? creating?) - [:div.counter (str @size "/300")])]]))) + [:div {:class (stl/css :counter)} (str @size "/300")])]]))) (mf/defc component-swap-item {::mf/wrap-props false} @@ -157,22 +177,21 @@ (st/emit! (dwl/component-multi-swap shapes file-id (:id item))))) item-ref (mf/use-ref) visible? (h/use-visible item-ref :once? true)] - [:div - {:ref item-ref - :title (if is-search (:full-name item) (:name item)) - :class (stl/css-case :component-item (not listing-thumbs) - :grid-cell listing-thumbs - :selected (= (:id item) component-id) - :disabled loop) - :key (str "swap-item-" (:id item)) - :on-click on-select-component} + [:div {:ref item-ref + :title (if is-search (:full-name item) (:name item)) + :class (stl/css-case :component-item (not listing-thumbs) + :grid-cell listing-thumbs + :selected (= (:id item) component-id) + :disabled loop) + :key (str "swap-item-" (:id item)) + :on-click on-select-component} (when visible? [:& cmm/component-item-thumbnail {:file-id (:file-id item) :root-shape root-shape :component item :container container}]) - [:span - {:class (stl/css-case :component-name true :selected (= (:id item) component-id))} + [:span {:class (stl/css-case :component-name true + :selected (= (:id item) component-id))} (if is-search (:full-name item) (:name item))]])) (mf/defc component-group-item @@ -185,9 +204,11 @@ :title group-name} [:div (when-not (str/blank? path) - [:span {:class (stl/css :component-group-path)} (str "\u00A0/\u00A0" path)]) - [:span {:class (stl/css :component-group-name)} (cfh/last-path group-name)]] - [:span i/arrow-slide]])) + [:span {:class (stl/css :component-group-path)} + (str "\u00A0/\u00A0" path)]) + [:span {:class (stl/css :component-group-name)} + (cfh/last-path group-name)]] + [:span i/arrow-refactor]])) (mf/defc component-swap [{:keys [shapes] :as props}] @@ -328,11 +349,10 @@ :icon (mf/html [:span {:class (stl/css :search-icon)} i/search-refactor])}]] [:div {:class (stl/css :select-field)} - [:& select - {:class (stl/css :select-library) - :default-value current-library-id - :options libraries-options - :on-change on-library-change}]] + [:& select {:class (stl/css :select-library) + :default-value current-library-id + :options libraries-options + :on-change on-library-change}]] [:div {:class (stl/css :library-name)} current-library-name] @@ -356,7 +376,7 @@ [:button {:class (stl/css :component-path) :on-click on-go-back :title (:path filters)} - [:span i/arrow-slide] + [:span i/arrow-refactor] [:span (:path filters)]]) (when (empty? items) @@ -462,7 +482,7 @@ (if swap-opened? [:button {:class (stl/css :title-back) :on-click on-component-back} - [:span i/arrow-slide] + [:span i/arrow-refactor] [:span (tr "workspace.options.component")]] [:& title-bar {:collapsable? true :collapsed? (not open?) @@ -473,7 +493,9 @@ (when open? [:div {:class (stl/css :element-content)} [:div {:class (stl/css :component-wrapper)} - [:div {:class (stl/css-case :component-name-wrapper true :with-main (and can-swap? (not multi)) :swappeable (and can-swap? (not swap-opened?))) + [:div {:class (stl/css-case :component-name-wrapper true + :with-main (and can-swap? (not multi)) + :swappeable (and can-swap? (not swap-opened?))) :on-click open-component-panel} [:span {:class (stl/css :component-icon)} (if main-instance? diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss index 61d3282994..7c620a2f47 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss @@ -7,249 +7,260 @@ @import "refactor/common-refactor.scss"; .element-set { margin: 0; - .element-content { - @include flexColumn; - margin-bottom: $s-8; - .component-wrapper { - display: flex; - margin: 0 $s-4 0 $s-8; - .component-name-wrapper { - @extend .asset-element; - @include flexRow; - flex-grow: 1; - height: 100%; - width: 100%; - flex-wrap: wrap; - padding: 0 0 0 $s-12; - margin-top: $s-8; +} - &.with-main { - padding-bottom: $s-12; - } +.element-content { + @include flexColumn; + margin-bottom: $s-8; +} - .component-icon { - @include flexCenter; - height: $s-24; - width: $s-24; - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - } - } - .component-name { - @include titleTipography; - @include textEllipsis; - width: 70%; - flex-grow: 2; - margin-left: $s-8; - } - .component-parent-name { - @include titleTipography; - @include textEllipsis; - padding-left: $s-36; - color: var(--title-foreground-color); - } - } - .swappeable { - cursor: pointer; - } - .component-actions { - position: relative; +.component-wrapper { + display: flex; + margin: 0 $s-4 0 $s-8; +} - .menu-btn { - @extend .button-tertiary; - height: $s-32; - width: $s-28; - svg { - @extend .button-icon; - } - } - .custom-select-dropdown { - @extend .dropdown-wrapper; - right: 0; - left: unset; - width: $s-252; - .dropdown-element { - @extend .dropdown-element-base; - } - } - } - } +.component-name-wrapper { + @extend .asset-element; + @include flexRow; + flex-grow: 1; + height: 100%; + width: 100%; + flex-wrap: wrap; + padding: 0 0 0 $s-12; + margin-top: $s-8; + + &.with-main { + padding-bottom: $s-12; } +} - .title-back { - @include tabTitleTipography; - cursor: pointer; - width: 100%; - background-color: var(--title-background-color); - color: var(--title-foreground-color); - text-align: left; - border: 0; - margin-bottom: $s-16; +.component-icon { + @include flexCenter; + height: $s-24; + width: $s-24; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } +} + +.component-name { + @include titleTipography; + @include textEllipsis; + width: 70%; + flex-grow: 2; + margin-left: $s-8; +} + +.component-parent-name { + @include titleTipography; + @include textEllipsis; + padding-left: $s-36; + color: var(--title-foreground-color); +} + +.swappeable { + cursor: pointer; +} + +.component-actions { + position: relative; +} + +.menu-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } +} + +.custom-select-dropdown { + @extend .dropdown-wrapper; + right: 0; + left: unset; + width: $s-252; +} + +.dropdown-element { + @extend .dropdown-element-base; +} + +.title-back { + @include tabTitleTipography; + cursor: pointer; + width: 100%; + background-color: var(--title-background-color); + color: var(--title-foreground-color); + text-align: left; + border: 0; + margin-bottom: $s-16; + svg { + height: $s-8; + width: $s-8; + stroke: var(--icon-foreground); + margin-right: $s-16; + transform: rotate(180deg); + } +} + +.search-field { + display: flex; + align-items: center; + height: $s-32; + margin: $s-16 $s-4 $s-4 $s-12; + border-radius: $br-8; + font-family: "worksans", sans-serif; + background-color: var(--input-background-color); +} + +.search-box { + align-items: center; + display: flex; + width: 100%; +} + +.icon-wrapper { + display: flex; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } +} + +.input-text { + @include removeInputStyle; + height: $s-32; + width: 100%; + margin: 0; + padding: $s-4; + border: 0; + font-size: $fs-12; + color: var(--input-foreground-color-active); + &::placeholder { + color: var(--input-foreground-color-disabled); + } + &:focus-visible { + border-color: var(--input-border-outline-color-active); + } +} + +.clear-btn { + @include buttonStyle; + @include flexCenter; + height: $s-16; + width: $s-16; + .clear-icon { + @include flexCenter; svg { - height: $s-8; - width: $s-8; - fill: var(--icon-foreground); - margin-right: $s-16; - transform: rotate(180deg); + @extend .button-icon-small; + stroke: var(--icon-foreground); } } +} - .component-swap { - .search-field { - display: flex; - align-items: center; - height: $s-32; - margin: $s-16 $s-4 $s-4 $s-12; - border-radius: $br-8; - font-family: "worksans", sans-serif; - background-color: var(--input-background-color); - .search-box { - align-items: center; - display: flex; - width: 100%; +.search-icon { + @include flexCenter; + width: $s-28; + svg { + @extend .button-icon-small; + stroke: var(--icon-foreground); + } +} - .icon-wrapper { - display: flex; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } +.select-field { + margin: $s-8 $s-4 0 $s-12; +} - .input-text { - @include removeInputStyle; - height: $s-32; - width: 100%; - margin: 0; - padding: $s-4; - border: 0; - font-size: $fs-12; - color: var(--input-foreground-color-active); - &::placeholder { - color: var(--input-foreground-color-disabled); - } - &:focus-visible { - border-color: var(--input-border-outline-color-active); - } - } - .clear-btn { - @include buttonStyle; - @include flexCenter; - height: $s-16; - width: $s-16; - .clear-icon { - @include flexCenter; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } - } - } - .search-icon { - @include flexCenter; - width: $s-28; - svg { - @extend .button-icon-small; - stroke: var(--icon-foreground); - } - } - } +.select-library { + padding-left: $s-20; +} - .select-field { - margin: $s-8 $s-4 0 $s-12; - } +.listing-options-wrapper { + width: 100%; +} - .select-library { - padding-left: $s-20; - } +.listing-options { + margin-left: auto; + margin-right: $s-4; +} - .listing-options-wrapper { - width: 100%; - } +.component-path { + @include titleTipography; + @include textEllipsis; + text-align: left; + cursor: pointer; + width: 100%; + background-color: var(--title-background-color); + color: var(--title-foreground-color); + border: 0; + margin: $s-16 0 $s-12 0; + padding: 0 $s-16 0 $s-24; + svg { + height: $s-8; + width: $s-8; + stroke: var(--icon-foreground); + margin-right: $s-16; + transform: rotate(180deg); + } +} - .listing-options { - margin-left: auto; - margin-right: $s-4; - } +.component-path-empty { + height: $s-16; +} - .component-path { - @include titleTipography; - @include textEllipsis; - text-align: left; - cursor: pointer; - width: 100%; - background-color: var(--title-background-color); - color: var(--title-foreground-color); - border: 0; - margin: $s-16 0 $s-12 0; - padding: 0 $s-16 0 $s-24; - svg { - height: $s-8; - width: $s-8; - fill: var(--icon-foreground); - margin-right: $s-16; - transform: rotate(180deg); - } - } +.component-list-empty { + @include titleTipography; + margin: 0 $s-4 0 $s-8; +} - .component-path-empty { - height: $s-16; - } +.component-list { + margin: 0 $s-4 0 $s-8; +} - .component-list-empty { - @include titleTipography; - margin: 0 $s-4 0 $s-8; - } +.component-item { + display: flex; + align-items: center; + margin-bottom: $s-4; + font-size: $s-12; + cursor: pointer; + width: 100%; + height: $s-36; + border-radius: $br-8; + background-color: var(--assets-item-background-color); + color: var(--assets-item-name-foreground-color); - .component-list { - margin: 0 $s-4 0 $s-8; - .component-item { - display: flex; - align-items: center; - margin-bottom: $s-4; - font-size: $s-12; - cursor: pointer; - width: 100%; - height: $s-36; - border-radius: $br-8; - background-color: var(--assets-item-background-color); - color: var(--assets-item-name-foreground-color); + .component-name { + @include textEllipsis; + width: 80%; + } - .component-name { - @include textEllipsis; - width: 80%; - } + svg, + img { + background-color: var(--assets-component-background-color); + border-radius: $br-8; + height: $s-32; + width: $s-32; + margin: $s-2 $s-8 $s-2 $s-2; + } - svg, - img { - background-color: var(--assets-component-background-color); - border-radius: $br-8; - height: $s-32; - width: $s-32; - margin: $s-2 $s-8 $s-2 $s-2; - } + .selected { + color: var(--assets-item-name-foreground-color-hover); + } - .selected { - color: var(--assets-item-name-foreground-color-hover); - } + &:hover { + color: var(--assets-item-name-foreground-color-hover); + background-color: var(--assets-item-background-color-hover); + } - &:hover { - color: var(--assets-item-name-foreground-color-hover); - background-color: var(--assets-item-background-color-hover); - } + &.disabled { + cursor: auto; + color: var(--assets-item-name-foreground-color-disabled); + background-color: var(--assets-item-background-color); - &.disabled { - cursor: auto; - color: var(--assets-item-name-foreground-color-disabled); - background-color: var(--assets-item-background-color); - - svg { - cursor: auto; - } - } - } + svg { + cursor: auto; } } } @@ -390,3 +401,172 @@ cursor: pointer; } } + +// Component annotation + +.component-annotation { + @include titleTipography; + color: var(--entry-foreground-color); + border-radius: $br-8; + + .annotation-title { + display: flex; + align-items: center; + height: $s-32; + + &.expanded { + border-bottom: $s-1 solid var(--entry-border-color-disabled); + } + + &.expandeable { + cursor: pointer; + } + + div { + display: flex; + align-items: center; + line-height: 2.5; + } + + .icon-arrow { + @include flexCenter; + width: $s-28; + height: $s-32; + display: flex; + margin: 0; + padding: 0; + cursor: pointer; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + width: $s-16; + height: $s-16; + } + &.expanded svg { + transform: rotate(90deg); + } + } + + .icon { + @include flexCenter; + width: $s-28; + height: $s-32; + border-radius: $br-8; + display: none; + margin: 0; + padding: 0; + cursor: pointer; + + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + width: $s-16; + height: $s-16; + } + + &.icon-tick:hover, + &.icon-edit:hover { + svg { + stroke: var(--icon-foreground-accept); + } + } + + &.icon-cross:hover, + &.icon-trash:hover { + svg { + stroke: var(--icon-foreground-discard); + } + } + } + + .annotation-text { + flex-grow: 1; + margin-left: $s-12; + } + + &:hover { + .icon { + display: flex; + } + } + } + + &.editing { + border: $s-1 solid var(--input-border-color-success); + .annotation-title { + border-bottom: $s-1 solid var(--entry-border-color-disabled); + .icon { + display: flex; + } + } + + textarea { + min-height: $s-252; + } + } + + &.creating { + border: $s-1 solid var(--input-border-color-success); + .annotation-title .icon { + display: flex; + } + textarea { + min-height: $s-252; + } + } + + .hidden { + display: none; + svg { + display: none; + } + } + + .counter { + @include titleTipography; + text-align: right; + color: var(--entry-foreground-color); + margin: 0 $s-8 $s-8 0; + } + + // Auto growing text + .grow-wrap { + // easy way to plop the elements on top of each other and have them both sized based on the tallest one's height + display: grid; + + &:after { + // The space is needed to preventy jumpy behavior + content: attr(data-replicated-value) " "; + white-space: pre-wrap; + visibility: hidden; + } + + textarea { + background-color: var(--input-background-color-active); + color: var(--input-foreground-color-active); + padding: $s-12; + + border: none; + overflow: hidden; + outline: none; + + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; + + resize: none; /*remove the resize handle on the bottom right*/ + } + + textarea, + &:after { + /* Identical styling required!! */ + font: inherit; + overflow-wrap: anywhere; + + padding: 10px; + + /* Place on top of each other */ + grid-area: 1 / 1 / 2 / 2; + } + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss b/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss index 209ed6a3b2..fef43646a2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.scss @@ -38,6 +38,7 @@ @include titleTipography; height: $s-20; padding: $s-4 $s-6; + margin-right: $s-12; border: $s-1 solid var(--tag-background-color); border-radius: $br-6; color: var(--tag-background-color); diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index 998a581ebc..c6b3947d58 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -547,6 +547,11 @@ [] (partition 2 params)))) +(defn ^boolean id? + [node id] + (when (some? node) + (= (.-id ^js node) id))) + (defn ^boolean class? [node class-name] (when (some? node) (let [class-list (.-classList ^js node)] diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index f944e0e701..56b36406af 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -243,6 +243,12 @@ (prn (str (:name frame) " - " (:id frame)))) nil)) +(defn ^:export select-by-object-id + [object-id] + (let [[_ page-id shape-id _] (str/split object-id #"/")] + (st/emit! (dw/go-to-page (uuid/uuid page-id))) + (st/emit! (dws/select-shape (uuid/uuid shape-id))))) + (defn ^:export select-by-id [shape-id] (st/emit! (dws/select-shape (uuid/uuid shape-id))))