diff --git a/frontend/src/app/main/ui/viewer/inspect/left_sidebar.cljs b/frontend/src/app/main/ui/viewer/inspect/left_sidebar.cljs index 9ab407a626..51c3d9dc7f 100644 --- a/frontend/src/app/main/ui/viewer/inspect/left_sidebar.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/left_sidebar.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.viewer.inspect.left-sidebar + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -15,7 +16,9 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.shape-icon :as si] + [app.main.ui.context :as ctx] [app.main.ui.icons :as i] + [app.main.ui.workspace.sidebar.layer-item :refer [layer-item-inner]] [app.main.ui.workspace.sidebar.layer-name :refer [layer-name]] [app.util.dom :as dom] [app.util.keyboard :as kbd] @@ -28,43 +31,55 @@ (l/derived st/state))) (mf/defc layer-item - [{:keys [item selected objects disable-collapse?] :as props}] - (let [id (:id item) - name (:name item) - hidden? (:hidden item) - touched? (-> item :touched seq boolean) + [{:keys [item selected objects disable-collapse? depth component-child? hide-toggle?] :as props}] + (let [new-css-system (mf/use-ctx ctx/new-css-system) + id (:id item) + name (:name item) + hidden? (:hidden item) + touched? (-> item :touched seq boolean) selected? (contains? selected id) item-ref (mf/use-ref nil) + depth (+ depth 1) - file (mf/deref refs/viewer-file) - components-v2 (dm/get-in file [:data :options :components-v2]) - main-instance? (if components-v2 - (ctk/main-instance? item) - true) - collapsed-iref (mf/use-memo - (mf/deps id) - (make-collapsed-iref id)) + file (mf/deref refs/viewer-file) + components-v2 (dm/get-in file [:data :options :components-v2]) + + main-instance? + (if components-v2 + (ctk/main-instance? item) + true) + + component-tree? (or component-child? (:component-root item)) + + collapsed-iref + (mf/use-memo + (mf/deps id) + (make-collapsed-iref id)) expanded? (not (mf/deref collapsed-iref)) absolute? (ctl/item-absolute? item) + toggle-collapse - (fn [event] - (dom/stop-propagation event) - (st/emit! (dv/toggle-collapse id))) + (mf/use-callback + (mf/deps id) + (fn [event] + (dom/stop-propagation event) + (st/emit! (dv/toggle-collapse id)))) select-shape - (fn [event] - (dom/prevent-default event) - (let [id (:id item)] - (cond - (kbd/mod? event) - (st/emit! (dv/toggle-selection id)) + (mf/use-callback + (mf/deps id) + (fn [event] + (dom/prevent-default event) + (cond + (kbd/mod? event) + (st/emit! (dv/toggle-selection id)) - (kbd/shift? event) - (st/emit! (dv/shift-select-to id)) + (kbd/shift? event) + (st/emit! (dv/shift-select-to id)) - :else - (st/emit! (dv/select-shape id)))))] + :else + (st/emit! (dv/select-shape id)))))] (mf/use-effect (mf/deps selected) @@ -72,55 +87,102 @@ (when (and (= (count selected) 1) selected?) (dom/scroll-into-view-if-needed! (mf/ref-val item-ref) true)))) - [:li {:ref item-ref - :class (dom/classnames - :component (not (nil? (:component-id item))) - :masked (:masked-group item) - :selected selected?)} + (if new-css-system + [:& layer-item-inner + {:ref item-ref + :item item + :depth depth + :read-only? true + :highlighted? false + :selected? selected? + :component-tree? component-tree? + :hidden? hidden? + :filtered? false + :expanded? expanded? + :hide-toggle? hide-toggle? + :on-select-shape select-shape + :on-toggle-collapse toggle-collapse} - [:div.element-list-body {:class (dom/classnames :selected selected? - :icon-layer (= (:type item) :icon)) - :on-click select-shape} - [:div.icon - (when absolute? - [:div.absolute i/position-absolute]) - [:& si/element-icon {:shape item :main-instance? main-instance?}]] - [:& layer-name {:shape-id id - :shape-name name - :shape-touched? touched? - :hidden? hidden? - :selected? selected? - :type-frame (cfh/frame-shape? item) - :disabled-double-click true}] + (when (and (:shapes item) expanded?) + [:div {:class (stl/css-case + :element-children true + :parent-selected selected?)} + (for [[index id] (reverse (d/enumerate (:shapes item)))] + (when-let [item (get objects id)] + [:& layer-item + {:item item + :selected selected + :index index + :objects objects + :key (dm/str id) + :depth depth + :component-child? component-tree?}]))])] - (when (and (not disable-collapse?) (:shapes item)) - [:span.toggle-content - {:on-click toggle-collapse - :class (when expanded? "inverse")} - i/arrow-slide])] + ;; OLD + [:li {:ref item-ref + :class (dom/classnames + :component (not (nil? (:component-id item))) + :masked (:masked-group item) + :selected selected?)} - (when (and (:shapes item) expanded?) - [:ul.element-children - (for [[index id] (reverse (d/enumerate (:shapes item)))] - (when-let [item (get objects id)] - [:& layer-item - {:item item - :selected selected - :index index - :objects objects - :key (:id item)}]))])])) + [:div.element-list-body {:class (dom/classnames :selected selected? + :icon-layer (= (:type item) :icon)) + :on-click select-shape} + [:div.icon + (when absolute? + [:div.absolute i/position-absolute]) + [:& si/element-icon {:shape item :main-instance? main-instance?}]] + [:& layer-name {:shape-id id + :shape-name name + :shape-touched? touched? + :hidden? hidden? + :selected? selected? + :type-frame (cfh/frame-shape? item) + :disabled-double-click true}] + + (when (and (not disable-collapse?) (:shapes item)) + [:span.toggle-content + {:on-click toggle-collapse + :class (when expanded? "inverse")} + i/arrow-slide])] + + (when (and (:shapes item) expanded?) + [:ul.element-children + (for [[index id] (reverse (d/enumerate (:shapes item)))] + (when-let [item (get objects id)] + [:& layer-item + {:item item + :selected selected + :index index + :objects objects + :key (:id item)}]))])]))) (mf/defc left-sidebar [{:keys [frame page local]}] - (let [selected (:selected local) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + selected (:selected local) objects (:objects page)] - [:aside.settings-bar.settings-bar-left - [:div.settings-bar-inside - [:ul.element-list - [:& layer-item - {:item frame - :selected selected - :index 0 - :objects objects - :disable-collapse? true}]]]])) + (if new-css-system + [:aside {:class (stl/css :settings-bar-left)} + [:div {:class (stl/css :settings-bar-inside)} + [:div {:class (stl/css :element-list)} + [:& layer-item + {:item frame + :selected selected + :index 0 + :objects objects + :sortable? false + :filtered? false + :depth -2 + :hide-toggle? true}]]]] + + [:aside.settings-bar.settings-bar-left + [:div.settings-bar-inside + [:ul.element-list + [:& layer-item + {:item frame + :selected selected + :index 0 + :objects objects + :disable-collapse? true}]]]]))) diff --git a/frontend/src/app/main/ui/viewer/inspect/left_sidebar.scss b/frontend/src/app/main/ui/viewer/inspect/left_sidebar.scss new file mode 100644 index 0000000000..889d480ad1 --- /dev/null +++ b/frontend/src/app/main/ui/viewer/inspect/left_sidebar.scss @@ -0,0 +1,22 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. +// +// Copyright (c) KALEIDOS INC + +@use "common/refactor/common-refactor.scss" as *; + +.settings-bar-left { + background-color: $db-primary; + height: 100%; + width: $s-256; +} + +.settings-bar-inside { + display: grid; + grid-template-columns: 100%; + grid-template-rows: 100%; + height: calc(100% - $s-2); + overflow-y: auto; + padding-top: $s-8; +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs index 0d65094eb0..ea01a4baee 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs @@ -32,6 +32,129 @@ [okulary.core :as l] [rumext.v2 :as mf])) +(mf/defc layer-item-inner + {::mf/wrap-props false + ::mf/forward-ref true} + [{:keys [item depth parent-size name-ref children + ;; Flags + read-only? highlighted? selected? component-tree? + filtered? expanded? dnd-over? dnd-over-top? dnd-over-bot? hide-toggle? + ;; Callbacks + on-select-shape on-context-menu on-pointer-enter on-pointer-leave on-zoom-to-selected + on-toggle-collapse on-enable-drag on-disable-drag on-toggle-visibility on-toggle-blocking]} + dref] + + (let [id (:id item) + name (:name item) + blocked? (:blocked item) + hidden? (:hidden item) + has-shapes? (-> item :shapes seq boolean) + touched? (-> item :touched seq boolean) + parent-board? (and (cfh/frame-shape? item) + (= uuid/zero (:parent-id item))) + absolute? (ctl/item-absolute? item) + components-v2 (mf/use-ctx ctx/components-v2) + main-instance? (or (not components-v2) (:main-instance item))] + [:* + [:div {:id id + :ref dref + :on-click on-select-shape + :on-context-menu on-context-menu + :class (stl/css-case + :layer-row true + :highlight highlighted? + :component (some? (:component-id item)) + :masked (:masked-group item) + :selected selected? + :type-frame (cfh/frame-shape? item) + :type-bool (cfh/bool-shape? item) + :type-comp component-tree? + :hidden hidden? + :dnd-over dnd-over? + :dnd-over-top dnd-over-top? + :dnd-over-bot dnd-over-bot? + :root-board parent-board?)} + [:span {:class (stl/css-case + :tab-indentation true + :filtered filtered?) + :style {"--depth" depth}}] + [:div {:class (stl/css-case + :element-list-body true + :filtered filtered? + :selected selected? + :icon-layer (= (:type item) :icon)) + :style {"--depth" depth} + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-double-click dom/stop-propagation} + + (if (< 0 (count (:shapes item))) + [:div {:class (stl/css :button-content)} + (when (and (not hide-toggle?) (not filtered?)) + [:button {:class (stl/css-case + :toggle-content true + :inverse expanded?) + :on-click on-toggle-collapse} + i/arrow-refactor]) + + [:div {:class (stl/css :icon-shape) + :on-double-click on-zoom-to-selected} + (when absolute? + [:div {:class (stl/css :absolute)}]) + + [:& sic/element-icon-refactor + {:shape item + :main-instance? main-instance?}]]] + + [:div {:class (stl/css :button-content)} + (when (not ^boolean filtered?) + [:span {:class (stl/css :toggle-content)}]) + [:div {:class (stl/css :icon-shape) + :on-double-click on-zoom-to-selected} + (when ^boolean absolute? + [:div {:class (stl/css :absolute)}]) + [:& sic/element-icon-refactor + {:shape item + :main-instance? main-instance?}]]]) + + [:& layer-name {:ref name-ref + :shape-id id + :shape-name name + :shape-touched? touched? + :disabled-double-click read-only? + :on-start-edit on-disable-drag + :on-stop-edit on-enable-drag + :depth depth + :parent-size parent-size + :selected? selected? + :type-comp component-tree? + :type-frame (cfh/frame-shape? item) + :hidden? hidden?}] + (when (not read-only?) + [:div {:class (stl/css-case + :element-actions true + :is-parent has-shapes? + :selected hidden? + :selected blocked?)} + [:button {:class (stl/css-case + :toggle-element true + :selected hidden?) + :title (if hidden? + (tr "workspace.shape.menu.show") + (tr "workspace.shape.menu.hide")) + :on-click on-toggle-visibility} + (if ^boolean hidden? i/hide-refactor i/shown-refactor)] + [:button {:class (stl/css-case + :block-element true + :selected blocked?) + :title (if (:blocked item) + (tr "workspace.shape.menu.unlock") + (tr "workspace.shape.menu.lock")) + :on-click on-toggle-blocking} + (if ^boolean blocked? i/lock-refactor i/unlock-refactor)]])]] + + children])) + (mf/defc layer-item {::mf/wrap-props false} [{:keys [index item selected objects sortable? filtered? depth parent-size component-child? highlighted]}] @@ -211,114 +334,44 @@ (let [scroll-to @scroll-to-middle?] (ts/schedule 100 - #(let [scroll-distance-ratio (dom/get-scroll-distance-ratio node scroll-node) - scroll-behavior (if (> scroll-distance-ratio 1) "instant" "smooth")] - (if scroll-to - (dom/scroll-into-view! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"}) - (do - (dom/scroll-into-view-if-needed! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"}) - (reset! scroll-to-middle? true)))))))] + #(when (and node scroll-node) + (let [scroll-distance-ratio (dom/get-scroll-distance-ratio node scroll-node) + scroll-behavior (if (> scroll-distance-ratio 1) "instant" "smooth")] + (if scroll-to + (dom/scroll-into-view! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"}) + (do + (dom/scroll-into-view-if-needed! first-child-node #js {:block "center" :behavior scroll-behavior :inline "start"}) + (reset! scroll-to-middle? true))))))))] #(when (some? subid) (rx/dispose! subid)))) (if new-css-system - [:* - [:div {:on-context-menu on-context-menu - :ref dref - :on-click select-shape - :id id - :class (stl/css-case - :layer-row true - :highlight highlighted? - :component (some? (:component-id item)) - :masked (:masked-group item) - :selected selected? - :type-frame (cfh/frame-shape? item) - :type-bool (cfh/bool-shape? item) - :type-comp component-tree? - :hidden hidden? - :dnd-over (= (:over dprops) :center) - :dnd-over-top (= (:over dprops) :top) - :dnd-over-bot (= (:over dprops) :bot) - :root-board parent-board?)} - [:span {:class (stl/css-case - :tab-indentation true - :filtered filtered?) - :style {"--depth" depth}}] - [:div {:class (stl/css-case - :element-list-body true - :filtered filtered? - :selected selected? - :icon-layer (= (:type item) :icon)) - :style {"--depth" depth} - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave - :on-double-click dom/stop-propagation} - - (if (< 0 (count (:shapes item))) - [:div {:class (stl/css :button-content)} - (when (not filtered?) - [:button {:class (stl/css-case - :toggle-content true - :inverse expanded?) - :on-click toggle-collapse} - i/arrow-refactor]) - - [:div {:class (stl/css :icon-shape) - :on-double-click zoom-to-selected} - (when absolute? - [:div {:class (stl/css :absolute)}]) - - [:& sic/element-icon-refactor - {:shape item - :main-instance? main-instance?}]]] - - [:div {:class (stl/css :button-content)} - (when (not ^boolean filtered?) - [:span {:class (stl/css :toggle-content)}]) - [:div {:class (stl/css :icon-shape) - :on-double-click zoom-to-selected} - (when ^boolean absolute? - [:div {:class (stl/css :absolute)}]) - [:& sic/element-icon-refactor - {:shape item - :main-instance? main-instance?}]]]) - - [:& layer-name {:ref ref - :shape-id id - :shape-name name - :shape-touched? touched? - :disabled-double-click read-only? - :on-start-edit disable-drag - :on-stop-edit enable-drag - :depth depth - :parent-size parent-size - :selected? selected? - :type-comp component-tree? - :type-frame (cfh/frame-shape? item) - :hidden? hidden?}] - [:div {:class (stl/css-case - :element-actions true - :is-parent has-shapes? - :selected hidden? - :selected blocked?)} - [:button {:class (stl/css-case - :toggle-element true - :selected hidden?) - :title (if hidden? - (tr "workspace.shape.menu.show") - (tr "workspace.shape.menu.hide")) - :on-click toggle-visibility} - (if ^boolean hidden? i/hide-refactor i/shown-refactor)] - [:button {:class (stl/css-case - :block-element true - :selected blocked?) - :title (if (:blocked item) - (tr "workspace.shape.menu.unlock") - (tr "workspace.shape.menu.lock")) - :on-click toggle-blocking} - (if ^boolean blocked? i/lock-refactor i/unlock-refactor)]]]] + [:& layer-item-inner + {:ref dref + :item item + :depth depth + :parent-size parent-size + :name-ref ref + :read-only? read-only? + :highlighted? highlighted? + :selected? selected? + :component-tree? component-tree? + :filtered? filtered? + :expanded? expanded? + :dnd-over? (= (:over dprops) :center) + :dnd-over-top? (= (:over dprops) :top) + :dnd-over-bot? (= (:over dprops) :bot) + :on-select-shape select-shape + :on-context-menu on-context-menu + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave + :on-zoom-to-selected zoom-to-selected + :on-toggle-collapse toggle-collapse + :on-enable-drag enable-drag + :on-disable-drag disable-drag + :on-toggle-visibility toggle-visibility + :on-toggle-blocking toggle-blocking} (when (and (:shapes item) expanded?) [:div {:class (stl/css-case @@ -326,7 +379,6 @@ :parent-selected selected? :sticky-children parent-board?) :data-id (when ^boolean parent-board? id)} - (for [[index id] (reverse (d/enumerate (:shapes item)))] (when-let [item (get objects id)] [:& layer-item diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss index 876205aec7..f3209a29dc 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.scss @@ -12,6 +12,7 @@ align-items: center; width: 100%; background-color: var(--layer-row-background-color); + border: 2px solid transparent; &.highlight, &:hover { @@ -43,6 +44,16 @@ .parent-selected &:hover { background-color: var(--layer-row-background-color-hover); } + + &.dnd-over-bot { + border-bottom: $s-2 solid var(--layer-row-foreground-color-hover); + } + &.dnd-over-top { + border-bottom: $s-2 solid var(--layer-row-foreground-color-hover); + } + &.dnd-over { + border: $s-2 solid var(--layer-row-foreground-color-hover); + } } .element-children { @@ -164,6 +175,9 @@ .layer-row.hidden & { opacity: $op-7; } + .layer-row.selected & { + stroke: var(--layer-row-foreground-color-selected); + } .layer-row.type-comp & { stroke: var(--layer-row-component-foreground-color); } @@ -172,10 +186,6 @@ opacity: $op-10; stroke: var(--layer-row-foreground-color-hover); } - - .layer-row.selected & { - stroke: var(--layer-row-foreground-color-selected); - } } .layer-row.selected & { diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 335676754b..60f26c46d2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -47,7 +47,7 @@ highlighted (hooks/use-equal-memo highlighted) root (get objects uuid/zero) new-css-system (mf/use-ctx ctx/new-css-system)] - [:ul + [:div {:class (stl/css new-css-system :element-list)} [:& hooks/sortable-container {} (for [[index id] (reverse (d/enumerate (:shapes root)))]