diff --git a/CHANGES.md b/CHANGES.md index 9870342acc..d0eaaef37f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -37,6 +37,7 @@ - Switch several variant copies at the same time [Taiga #11411](https://tree.taiga.io/project/penpot/us/11411) - Invitations management improvements [Taiga #3479](https://tree.taiga.io/project/penpot/us/3479) - Alternative ways of creating variants - Button Viewport [Taiga #11931](https://tree.taiga.io/project/penpot/us/11931) +- Reorder properties for a component [Taiga #10225](https://tree.taiga.io/project/penpot/us/10225) ### :bug: Bugs fixed diff --git a/common/src/app/common/logic/variant_properties.cljc b/common/src/app/common/logic/variant_properties.cljc index 0e43fe6c38..1a066cd096 100644 --- a/common/src/app/common/logic/variant_properties.cljc +++ b/common/src/app/common/logic/variant_properties.cljc @@ -81,6 +81,26 @@ #(assoc % :variant-error value)))))) +(defn generate-reorder-variant-poperties + [changes variant-id from-pos to-space-between-pos] + (let [data (pcb/get-library-data changes) + objects (pcb/get-objects changes) + related-components (cfv/find-variant-components data objects variant-id)] + (reduce (fn [changes component] + (let [props (:variant-properties component) + props (ctv/reorder-by-moving-to-position props from-pos to-space-between-pos) + main-id (:main-instance-id component) + name (ctv/properties-to-name props)] + (-> changes + (pcb/update-component (:id component) + #(assoc % :variant-properties props) + {:apply-changes-local-library? true}) + (pcb/update-shapes [main-id] + #(assoc % :variant-name name))))) + changes + related-components))) + + (defn generate-add-new-property [changes variant-id & {:keys [fill-values? editing? property-name property-value]}] (let [data (pcb/get-library-data changes) diff --git a/common/src/app/common/types/variant.cljc b/common/src/app/common/types/variant.cljc index bbac03d24a..cfb90eeae0 100644 --- a/common/src/app/common/types/variant.cljc +++ b/common/src/app/common/types/variant.cljc @@ -310,3 +310,27 @@ the real name of the shape joined by the properties values separated by '/'" [variant] (cpn/merge-path-item (:name variant) (str/replace (:variant-name variant) #", " " / "))) + +(defn reorder-by-moving-to-position + "Reorder a vector by moving one of their items from some position to some space between positions. + It clamps the position numbers to a valid range." + [props from-pos to-space-between-pos] + (let [max-space-pos (count props) + max-prop-pos (dec max-space-pos) + + from-pos (max 0 (min max-prop-pos from-pos)) + to-space-between-pos (max 0 (min max-space-pos to-space-between-pos))] + + (if (= from-pos to-space-between-pos) + props + (let [elem (nth props from-pos) + without-elem (-> [] + (into (subvec props 0 from-pos)) + (into (subvec props (inc from-pos)))) + insert-pos (if (< from-pos to-space-between-pos) + (dec to-space-between-pos) + to-space-between-pos)] + (-> [] + (into (subvec without-elem 0 insert-pos)) + (into [elem]) + (into (subvec without-elem insert-pos))))))) diff --git a/common/test/common_tests/variant_test.cljc b/common/test/common_tests/variant_test.cljc index 05bc748703..71587e3e58 100644 --- a/common/test/common_tests/variant_test.cljc +++ b/common/test/common_tests/variant_test.cljc @@ -159,3 +159,48 @@ (t/testing "update-number-in-repeated-prop-names" (t/is (= (ctv/update-number-in-repeated-prop-names props) numbered-props))))) + + +(t/deftest reorder-by-moving-to-position + (let [props [{:name "border" :value "no"} + {:name "color" :value "blue"} + {:name "shadow" :value "yes"} + {:name "background" :value "none"}]] + + (t/testing "reorder-by-moving-to-position" + (t/is (= (ctv/reorder-by-moving-to-position props 0 2) [{:name "color" :value "blue"} + {:name "border" :value "no"} + {:name "shadow" :value "yes"} + {:name "background" :value "none"}])) + (t/is (= (ctv/reorder-by-moving-to-position props 0 3) [{:name "color" :value "blue"} + {:name "shadow" :value "yes"} + {:name "border" :value "no"} + {:name "background" :value "none"}])) + (t/is (= (ctv/reorder-by-moving-to-position props 0 4) [{:name "color" :value "blue"} + {:name "shadow" :value "yes"} + {:name "background" :value "none"} + {:name "border" :value "no"}])) + (t/is (= (ctv/reorder-by-moving-to-position props 3 0) [{:name "background" :value "none"} + {:name "border" :value "no"} + {:name "color" :value "blue"} + {:name "shadow" :value "yes"}])) + (t/is (= (ctv/reorder-by-moving-to-position props 3 2) [{:name "border" :value "no"} + {:name "color" :value "blue"} + {:name "background" :value "none"} + {:name "shadow" :value "yes"}])) + (t/is (= (ctv/reorder-by-moving-to-position props 0 5) [{:name "color" :value "blue"} + {:name "shadow" :value "yes"} + {:name "background" :value "none"} + {:name "border" :value "no"}])) + (t/is (= (ctv/reorder-by-moving-to-position props 3 -1) [{:name "background" :value "none"} + {:name "border" :value "no"} + {:name "color" :value "blue"} + {:name "shadow" :value "yes"}])) + (t/is (= (ctv/reorder-by-moving-to-position props 5 -1) [{:name "background" :value "none"} + {:name "border" :value "no"} + {:name "color" :value "blue"} + {:name "shadow" :value "yes"}])) + (t/is (= (ctv/reorder-by-moving-to-position props -1 5) [{:name "color" :value "blue"} + {:name "shadow" :value "yes"} + {:name "background" :value "none"} + {:name "border" :value "no"}]))))) diff --git a/frontend/playwright/ui/specs/workspace.spec.js b/frontend/playwright/ui/specs/workspace.spec.js index 107ea3189b..411d01d9a8 100644 --- a/frontend/playwright/ui/specs/workspace.spec.js +++ b/frontend/playwright/ui/specs/workspace.spec.js @@ -271,7 +271,7 @@ test("Bug 9066 - Problem with grid layout", async ({ page }) => { await workspacePage.clickToggableLayer("Group"); await page.getByText("A", { exact: true }).click(); - await workspacePage.rightSidebar.getByTestId("swap-component-btn").click(); + await workspacePage.rightSidebar.getByTestId("component-pill-button").click(); await page.getByTitle("C", { exact: true }).click(); diff --git a/frontend/src/app/main/data/workspace/variants.cljs b/frontend/src/app/main/data/workspace/variants.cljs index 2b3ec3a807..f536135cc3 100644 --- a/frontend/src/app/main/data/workspace/variants.cljs +++ b/frontend/src/app/main/data/workspace/variants.cljs @@ -262,6 +262,28 @@ (dch/commit-changes changes) (dwu/commit-undo-transaction undo-id)))))) +(defn reorder-variant-poperties + "Reorder properties by moving a property from some position to some space between positions" + [variant-id from-pos to-space-between-pos] + (ptk/reify ::reorder-variant-properties + ptk/WatchEvent + (watch [it state _] + (let [page-id (:current-page-id state) + data (dsh/lookup-file-data state) + objects (-> (dsh/get-page data page-id) + (get :objects)) + + changes (-> (pcb/empty-changes it page-id) + (pcb/with-library-data data) + (pcb/with-objects objects) + (clvp/generate-reorder-variant-poperties variant-id from-pos to-space-between-pos)) + + undo-id (js/Symbol)] + (rx/of + (dwu/start-undo-transaction undo-id) + (dch/commit-changes changes) + (dwu/commit-undo-transaction undo-id)))))) + (defn- set-variant-id "Sets the variant-id on a component" [component-id variant-id] diff --git a/frontend/src/app/main/ui/hooks.cljs b/frontend/src/app/main/ui/hooks.cljs index bfc2399d2a..a7ba644c8c 100644 --- a/frontend/src/app/main/ui/hooks.cljs +++ b/frontend/src/app/main/ui/hooks.cljs @@ -65,10 +65,10 @@ (def sortable-ctx (mf/create-context nil)) -(mf/defc sortable-container - [{:keys [children] :as props}] +(mf/defc sortable-container* + [{:keys [children]}] (let [global-drag-end (mf/use-memo #(rx/subject))] - [:& (mf/provider sortable-ctx) {:value global-drag-end} + [:> (mf/provider sortable-ctx) {:value global-drag-end} children])) diff --git a/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs index b88bb3042c..542ff2fec4 100644 --- a/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs +++ b/frontend/src/app/main/ui/workspace/colorpicker/gradients.cljs @@ -356,7 +356,7 @@ :icon i/add}]]] [:div {:class (stl/css :gradient-stops-list)} - [:& h/sortable-container {} + [:> h/sortable-container* {} (for [[index stop] (d/enumerate stops)] [:> stop-input-row* {:key index diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index dcc12bdb65..398473f54c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -72,7 +72,7 @@ highlighted (hooks/use-equal-memo highlighted) root (get objects uuid/zero)] [:div {:class (stl/css :element-list) :data-testid "layer-item"} - [:& hooks/sortable-container {} + [:> hooks/sortable-container* {} (for [[index id] (reverse (d/enumerate (:shapes root)))] (when-let [obj (get objects id)] (if (cfh/frame-shape? obj) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options.cljs b/frontend/src/app/main/ui/workspace/sidebar/options.cljs index 1b73dbbc34..b5c0480773 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options.cljs @@ -23,7 +23,7 @@ [app.main.ui.workspace.sidebar.options.drawing :as drawing] [app.main.ui.workspace.sidebar.options.menus.align :refer [align-options*]] [app.main.ui.workspace.sidebar.options.menus.bool :refer [bool-options*]] - [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]] + [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu*]] [app.main.ui.workspace.sidebar.options.menus.grid-cell :as grid-cell] [app.main.ui.workspace.sidebar.options.menus.interactions :refer [interactions-menu]] [app.main.ui.workspace.sidebar.options.menus.layout-container :as layout-container] @@ -89,7 +89,7 @@ {::mf/private true} [{:keys [panel]}] (when (= (:type panel) :component-swap) - [:& component-menu {:shapes (:shapes panel) :swap-opened? true}])) + [:> component-menu* {:shapes (:shapes panel) :is-swap-opened true}])) (mf/defc design-menu* {::mf/private true} 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 6bb094b24b..b34552e7f2 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 @@ -29,10 +29,12 @@ [app.main.store :as st] [app.main.ui.components.dropdown :refer [dropdown]] [app.main.ui.components.radio-buttons :refer [radio-button radio-buttons]] + [app.main.ui.components.reorder-handler :refer [reorder-handler*]] [app.main.ui.components.search-bar :refer [search-bar*]] [app.main.ui.components.select :refer [select]] [app.main.ui.components.title-bar :refer [title-bar*]] [app.main.ui.context :as ctx] + [app.main.ui.ds.buttons.button :refer [button*]] [app.main.ui.ds.buttons.icon-button :refer [icon-button*]] [app.main.ui.ds.controls.combobox :refer [combobox*]] [app.main.ui.ds.controls.select :refer [select*]] @@ -149,12 +151,11 @@ (dw/set-annotations-id-for-create nil)) (dw/update-component-annotation component-id nil) (rerender-fn)))] - (st/emit! (modal/show - {:type :confirm - :title (tr "modals.delete-component-annotation.title") - :message (tr "modals.delete-component-annotation.message") - :accept-label (tr "ds.confirm-ok") - :on-accept on-accept})))))] + (st/emit! (modal/show {:type :confirm + :title (tr "modals.delete-component-annotation.title") + :message (tr "modals.delete-component-annotation.message") + :accept-label (tr "ds.confirm-ok") + :on-accept on-accept})))))] (mf/with-effect [shape-id state create-id creating?] (when-let [textarea (mf/ref-val textarea-ref)] @@ -171,31 +172,28 @@ (st/emit! (dw/set-annotations-id-for-create nil))))) (when (or creating? annotation) - [: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 expanded?) + [:div {:class (stl/css-case :annotation true + :editing editing? + :creating creating?)} + [:div {:class (stl/css-case :annotation-title true + :expandeable (not (or editing? creating?)) + :expanded expanded?) :on-click on-toggle-expand} (if (or editing? creating?) - [:span {:class (stl/css :annotation-text)} + [:span {:class (stl/css :annotation-title-name)} (if editing? (tr "workspace.options.component.edit-annotation") (tr "workspace.options.component.create-annotation"))] [:* - [:span {:class (stl/css-case - :icon-arrow true - :expanded expanded?)} - deprecated-icon/arrow] - [:span {:class (stl/css :annotation-text)} + [:> icon* {:icon-id (if expanded? i/arrow-down i/arrow-right) + :class (stl/css :annotation-title-icon-arrow) + :size "s"}] + [:span {:class (stl/css :annotation-title-name)} (tr "workspace.options.component.annotation")]]) - [:div {:class (stl/css :icons-wrapper)} + [:div {:class (stl/css :annotation-title-actions)} (when (and ^boolean main-instance? ^boolean expanded?) (if (or ^boolean editing? @@ -205,40 +203,41 @@ (tr "labels.create") (tr "labels.save")) :on-click on-save - :class (stl/css-case - :icon true - :icon-tick true - :invalid invalid-text?)} - deprecated-icon/tick] - [:div {:class (stl/css :icon :icon-cross) + :class (stl/css :annotation-title-icon-action)} + [:> icon* {:icon-id i/tick + :class (stl/css-case :annotation-title-icon-ok true + :disabled invalid-text?)}]] + [:div {:class (stl/css :annotation-title-icon-action) :title (tr "labels.discard") :on-click on-discard} - deprecated-icon/close]] + [:> icon* {:icon-id i/close + :class (stl/css :annotation-title-icon-nok)}]]] [:* - [:div {:class (stl/css :icon :icon-edit) + [:div {:class (stl/css :annotation-title-icon-action) :title (tr "labels.edit") :on-click on-edit} - deprecated-icon/curve] - [:div {:class (stl/css :icon :icon-trash) + [:> icon* {:icon-id i/curve + :class (stl/css :annotation-title-icon-ok)}]] + [:div {:class (stl/css :annotation-title-icon-action) :title (tr "labels.delete") :on-click on-delete-annotation} - deprecated-icon/delete]]))]] + [:> icon* {:icon-id i/delete + :class (stl/css :annotation-title-icon-nok)}]]]))]] - [:div {:class (stl/css-case :hidden (not 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 (or editing? creating?) - :maxLength 300 - :on-input adjust-textarea-size - :default-value annotation - :read-only (not (or creating? editing?))}]] + [:div {:class (stl/css-case :annotation-body-hidden (not expanded?))} + [:div {:class (stl/css :annotation-body)} + [:textarea {:ref textarea-ref + :id "annotation-textarea" + :class (stl/css :annotation-textarea) + :data-debug annotation + :auto-focus (or editing? creating?) + :max-length 300 + :on-input adjust-textarea-size + :default-value annotation + :read-only (not (or creating? editing?))}]] (when (or editing? creating?) - [:div {:class (stl/css :counter)} (str size "/300")])]]))) + [:div {:class (stl/css :annotation-counter)} (str size "/300")])]]))) (defn- get-variant-malformed-warning-message "Receive a list of booleans, one for each selected variant, indicating if that variant @@ -300,7 +299,47 @@ (mapv (fn [val] {:id val :label (if (str/blank? val) (str "(" (tr "labels.empty") ")") val)})))) -(mf/defc component-variant-main-instance* +(mf/defc component-variant-property* + [{:keys [pos prop options on-prop-name-blur on-prop-value-change on-reorder]}] + (let [on-drop + (mf/use-fn + (fn [relative-pos data] + (let [from-pos (:from-pos data) + to-space-between-pos (if (= relative-pos :bot) (inc pos) pos)] + (on-reorder from-pos to-space-between-pos)))) + + [dprops dref] + (h/use-sortable + :data-type "penpot/variant-property" + :on-drop on-drop + :draggable? true + :data {:from-pos pos})] + + [:div {:class (stl/css-case :variant-property true + :dnd-over-top (= (:over dprops) :top) + :dnd-over-bot (= (:over dprops) :bot))} + (when (some? on-reorder) + [:> reorder-handler* {:ref dref}]) + + [:div {:class (stl/css :variant-property-container)} + [:div {:class (stl/css :variant-property-name-wrapper)} + [:> input-with-meta* {:value (:name prop) + :is-editing (:editing? (meta prop)) + :max-length ctv/property-max-length + :data-position pos + :on-blur on-prop-name-blur}]] + + [:div {:class (stl/css :variant-property-value-wrapper)} + (let [mixed-value? (= (:value prop) false)] + [:> combobox* {:id (str "variant-prop-" pos) + :placeholder (if mixed-value? (tr "settings.multiple") "--") + :default-selected (if mixed-value? "" (:value prop)) + :options options + :empty-to-end true + :max-length ctv/property-max-length + :on-change on-prop-value-change}])]]])) + +(mf/defc component-variant* [{:keys [components shapes data]}] (let [component (first components) @@ -356,33 +395,28 @@ int)] (when (seq value) (st/emit! - (dwv/update-property-name variant-id pos value {:trigger "workspace:design-tab-variant"}))))))] + (dwv/update-property-name variant-id pos value {:trigger "workspace:design-tab-variant"})))))) + + reorder-properties + (mf/use-fn + (mf/deps variant-id) + (fn [from-pos to-space-between-pos] + (st/emit! (dwv/reorder-variant-poperties variant-id from-pos to-space-between-pos))))] [:* - [:div {:class (stl/css :variant-property-list)} - (for [[pos prop] (map-indexed vector properties)] - [:div {:key (str variant-id "-" pos) - :class (stl/css :variant-property-container)} - - [:div {:class (stl/css :variant-property-name-wrapper)} - [:> input-with-meta* {:value (:name prop) - :is-editing (:editing? (meta prop)) - :max-length ctv/property-max-length - :data-position pos - :on-blur update-property-name}]] - - [:div {:class (stl/css :variant-property-value-wrapper)} - (let [mixed-value? (= (:value prop) false)] - [:> combobox* {:id (str "variant-prop-" variant-id "-" pos) - :placeholder (if mixed-value? (tr "settings.multiple") "--") - :default-selected (if mixed-value? "" (:value prop)) - :options (get-options (:name prop)) - :empty-to-end true - :max-length ctv/property-max-length - :on-change (partial update-property-value pos)}])]])] + [:> h/sortable-container* {} + [:div {:class (stl/css :variant-property-list)} + (for [[pos prop] (map-indexed vector properties)] + [:> component-variant-property* {:key (str variant-id "-" pos) + :pos pos + :prop prop + :options (get-options (:name prop)) + :on-prop-name-blur update-property-name + :on-prop-value-change (partial update-property-value pos) + :on-reorder reorder-properties}])]] (if malformed-msg - [:div {:class (stl/css :variant-warning-wrapper)} + [:div {:class (stl/css :variant-warning)} [:> icon* {:icon-id i/msg-neutral :class (stl/css :variant-warning-darken)}] [:div {:class (stl/css :variant-warning-highlight)} @@ -391,7 +425,7 @@ (tr "workspace.options.component.variant.malformed.structure.example")]] (when duplicated-msg - [:div {:class (stl/css :variant-warning-wrapper)} + [:div {:class (stl/css :variant-warning)} [:> icon* {:icon-id i/msg-neutral :class (stl/css :variant-warning-darken)}] [:div {:class (stl/css :variant-warning-highlight)} @@ -471,7 +505,7 @@ [:* [:div {:class (stl/css :variant-property-list)} - (for [[pos prop] (map vector (range) props-first)] + (for [[pos prop] (map-indexed vector props-first)] (let [mixed-value? (not-every? #(= (:value prop) (:value (nth % pos))) properties) options (cond-> (get-options (:name prop)) mixed-value? @@ -492,7 +526,7 @@ :key (str (:value prop) "-" key)}]]]))] (if (seq malformed-comps) - [:div {:class (stl/css :variant-warning-wrapper)} + [:div {:class (stl/css :variant-warning)} [:> icon* {:icon-id i/msg-neutral :class (stl/css :variant-warning-darken)}] [:div {:class (stl/css :variant-warning-highlight)} @@ -502,7 +536,7 @@ (tr "workspace.options.component.variant.malformed.locate")]] (when (seq duplicated-comps) - [:div {:class (stl/css :variant-warning-wrapper)} + [:div {:class (stl/css :variant-warning)} [:> icon* {:icon-id i/msg-neutral :class (stl/css :variant-warning-darken)}] [:div {:class (stl/css :variant-warning-highlight)} @@ -523,43 +557,40 @@ item-ref (mf/use-ref) visible? (h/use-visible item-ref :once? true)] - [:div {:ref item-ref - :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} + [:button {:ref item-ref + :key (str "swap-item-" (:id item)) + :class (stl/css-case :swap-item-list (not listing-thumbs) + :swap-item-grid listing-thumbs + :selected (= (:id item) component-id)) + :on-click on-select + :disabled loop} (when visible? - [:> cmm/component-item-thumbnail* - {:file-id (:file-id item) - :class (stl/css :component-img) - :root-shape root-shape - :component item - :container container}]) + [:> cmm/component-item-thumbnail* {:file-id (:file-id item) + :class (stl/css :swap-item-thumbnail) + :root-shape root-shape + :component item + :container container}]) [:span {:title (if is-search (:full-name item) (:name item)) - :class (stl/css-case :component-name true - :selected (= (:id item) component-id))} + :class (stl/css :swap-item-name)} (if is-search (:full-name item) (:name item))] (when (ctk/is-variant? item) - [:span {:class (stl/css-case :variant-mark-cell listing-thumbs - :variant-icon true) + [:span {:class (stl/css :swap-item-variant-icon) :title (tr "workspace.assets.components.num-variants" num-variants)} [:> icon* {:icon-id i/variant :size "s"}]])])) -(mf/defc component-group-item* +(mf/defc component-swap-group-title* [{:keys [item on-enter-group]}] - (let [group-name (:name item) + (let [group-name (:name item) on-group-click #(on-enter-group group-name)] - [:div {:class (stl/css :component-group) + [:div {:class (stl/css :swap-group) :on-click on-group-click :title group-name} - [:span {:class (stl/css :component-group-name)} + [:span {:class (stl/css :swap-group-name)} (cpn/last-path group-name)] - [:> icon* {:class (stl/css :component-group-icon) + [:> icon* {:class (stl/css :swap-group-icon) :variant "ghost" :icon-id i/arrow-right :size "s"}]])) @@ -621,7 +652,7 @@ filters (deref filters*) - is-search? (not (str/blank? (:term filters))) + search? (not (str/blank? (:term filters))) current-library-id (if (contains? libraries (:file-id filters)) (:file-id filters) @@ -662,15 +693,15 @@ (distinct) (filter #(= (cpn/butlast-path %) (:path filters)))) - groups (when-not is-search? + groups (when-not search? (->> (sort (sequence xform components)) (map (fn [name] {:name name})))) - components (if is-search? + components (if search? (filter #(str/includes? (str/lower (:full-name %)) (str/lower (:term filters))) components) (filter #(= (:path %) (:path filters)) components)) - items (if (or is-search? (:listing-thumbs? filters)) + items (if (or search? (:listing-thumbs? filters)) (sort-by :full-name components) (->> (concat groups components) (sort-by :name))) @@ -686,7 +717,8 @@ ;; Get the ids of the components that are parents of the shapes, to avoid loops parent-components (mapcat find-parent-components shapes) - libraries-options (map (fn [library] {:value (:id library) :label (:name library)}) + libraries-options (map (fn [library] {:value (:id library) + :label (:name library)}) (vals libraries)) on-library-change @@ -714,63 +746,60 @@ (mf/use-fn (fn [style] (swap! filters* assoc :listing-thumbs? (= style "grid")))) - filter-path-with-dots (->> (:path filters) (cpn/split-path) (cpn/join-path-with-dot))] - [:div {:class (stl/css :component-swap)} - [:div {:class (stl/css :element-set-title)} + filter-path-with-dots (->> (:path filters) + (cpn/split-path) + (cpn/join-path-with-dot))] + + [:div {:class (stl/css :swap)} + [:div {:class (stl/css :swap-title)} [:span (tr "workspace.options.component.swap")]] - [:div {:class (stl/css :component-swap-content)} - [:div {:class (stl/css :fields-wrapper)} - [:div {:class (stl/css :search-field)} - [:> search-bar* {:on-change on-search-term-change - :on-clear on-search-clear-click - :class (stl/css :search-wrapper) - :id "swap-component-search-filter" - :value (:term filters) - :placeholder (str (tr "labels.search") " " (get-in libraries [current-library-id :name])) - :icon-id i/search}]] - - [:& select {:class (stl/css :select-library) - :default-value current-library-id + [:div {:class (stl/css :swap-content)} + [:div {:class (stl/css :swap-filters)} + [:> search-bar* {:id "swap-component-search-filter" + :icon-id i/search + :value (:term filters) + :placeholder (str (tr "labels.search") " " (get-in libraries [current-library-id :name])) + :on-change on-search-term-change + :on-clear on-search-clear-click}] + [:& select {:default-value current-library-id :options libraries-options :on-change on-library-change}]] - [:div {:class (stl/css :swap-wrapper)} - [:div {:class (stl/css :library-name-wrapper)} - [:div {:class (stl/css :library-name)} current-lib-name] + [:div {:class (stl/css :swap-library)} + [:div {:class (stl/css :swap-library-title)} + [:div {:class (stl/css :swap-library-name)} current-lib-name] + [:& radio-buttons {:selected (if (:listing-thumbs? filters) "grid" "list") + :on-change toggle-list-style + :name "swap-listing-style"} + [:& radio-button {:icon deprecated-icon/view-as-list + :value "list" + :id "swap-opt-list"}] + [:& radio-button {:icon deprecated-icon/flex-grid + :value "grid" + :id "swap-opt-grid"}]]] - [:div {:class (stl/css :listing-options-wrapper)} - [:& radio-buttons {:class (stl/css :listing-options) - :selected (if (:listing-thumbs? filters) "grid" "list") - :on-change toggle-list-style - :name "swap-listing-style"} - [:& radio-button {:icon deprecated-icon/view-as-list - :value "list" - :id "swap-opt-list"}] - [:& radio-button {:icon deprecated-icon/flex-grid - :value "grid" - :id "swap-opt-grid"}]]]] - - (when-not (or is-search? (str/empty? (:path filters))) - [:button {:class (stl/css :component-path) + (when-not (or search? (str/empty? (:path filters))) + [:button {:class (stl/css :swap-library-back) :on-click on-go-back :title filter-path-with-dots} [:> icon* {:icon-id i/arrow-left :size "s"}] - [:span {:class (stl/css :path-name)} + [:span {:class (stl/css :swap-library-back-name)} filter-path-with-dots]]) (when (empty? items) - [:div {:class (stl/css :component-list-empty)} + [:div {:class (stl/css :swap-library-empty)} (tr "workspace.options.component.swap.empty")]) ;;TODO review this empty space (when (:listing-thumbs? filters) - [:div {:class (stl/css :component-list)} + [:div (for [item groups] - [:> component-group-item* {:item item :on-enter-group on-enter-group}])]) + [:> component-swap-group-title* {:item item + :on-enter-group on-enter-group}])]) - [:div {:class (stl/css-case :component-grid (:listing-thumbs? filters) - :component-list (not (:listing-thumbs? filters)))} + [:div {:class (stl/css-case :swap-library-grid (:listing-thumbs? filters) + :swap-library-list (not (:listing-thumbs? filters)))} ;; FIXME: This could be in the thousands. We need to think about paginate this (for [item items] (if (:id item) @@ -789,34 +818,78 @@ :root-shape root-shape :container container :component-id component-id - :is-search is-search? + :is-search search? :listing-thumbs (:listing-thumbs? filters) :num-variants (count-variants item)}]) - [:> component-group-item* {:item item - :key (:name item) - :on-enter-group on-enter-group}]))]]]])) + [:> component-swap-group-title* {:item item + :key (:name item) + :on-enter-group on-enter-group}]))]]]])) -(mf/defc component-ctx-menu* - [{:keys [menu-entries on-close show main-instance]}] - (let [do-action +(mf/defc component-pill* + [{:keys [icon text subtext menu-entries disabled on-click]}] + (let [menu-open* (mf/use-state false) + menu-open? (deref menu-open*) + + menu-entries? (seq menu-entries) + + on-menu-click + (mf/use-fn + (mf/deps menu-open* menu-open?) + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (reset! menu-open* (not menu-open?)))) + + on-menu-close + (mf/use-fn + (mf/deps menu-open*) + #(reset! menu-open* false)) + + do-action (fn [action event] (dom/stop-propagation event) (action) - (on-close))] - [:& dropdown {:show show :on-close on-close} - [:ul {:class (stl/css-case :custom-select-dropdown true - :not-main (not main-instance))} - (for [{:keys [title action]} menu-entries] - (when (some? title) - [:li {:key title - :class (stl/css :dropdown-element) - :on-click (partial do-action action)} - [:span {:class (stl/css :dropdown-label)} title]]))]])) + (on-menu-close))] -(mf/defc component-menu - {::mf/props :obj} - [{:keys [shapes swap-opened?]}] + [:div {:class (stl/css :pill)} + [:button {:class (stl/css-case :pill-btn true + :with-menu menu-entries?) + :data-testid "component-pill-button" + :on-click on-click + :disabled disabled} + + [:div {:class (stl/css :pill-btn-icon)} + [:> icon* {:size "s" + :icon-id icon}]] + + [:div {:class (stl/css :pill-btn-name)} + [:div {:class (stl/css :pill-btn-text)} + text] + (when subtext + [:div {:class (stl/css :pill-btn-subtext)} + subtext])]] + + (when menu-entries? + [:div {:class (stl/css :pill-actions)} + [:button {:class (stl/css-case :pill-actions-btn true + :selected menu-open?) + :on-click on-menu-click} + [:> icon* {:icon-id i/menu}]] + + [:& dropdown {:show menu-open? + :on-close on-menu-close} + [:ul {:class (stl/css-case :pill-actions-dropdown true + :extended subtext)} + (for [{:keys [title action]} menu-entries] + (when (some? title) + [:li {:key title + :class (stl/css :pill-actions-dropdown-item) + :on-click (partial do-action action)} + [:span title]]))]]])])) + +(mf/defc component-menu* + [{:keys [shapes is-swap-opened]}] (let [current-file-id (mf/use-ctx ctx/current-file-id) libraries (mf/deref refs/files) @@ -827,7 +900,6 @@ :menu-open false})) state (deref state*) open? (:show-content state) - menu-open? (:menu-open state) shapes (filter ctk/instance-head? shapes) multi (> (count shapes) 1) @@ -855,18 +927,8 @@ main-instance? (ctk/main-instance? shape) toggle-content - (mf/use-fn #(swap! state* update :show-content not)) - - on-menu-click (mf/use-fn - (fn [event] - (dom/prevent-default event) - (dom/stop-propagation event) - (swap! state* update :menu-open not))) - - on-menu-close - (mf/use-fn - #(swap! state* assoc :menu-open false)) + #(swap! state* update :show-content not)) on-click-variant-title-help (mf/use-fn @@ -923,14 +985,13 @@ (swap! state* update :render inc))) menu-entries (cmm/generate-components-menu-entries shapes {:for-design-tab? true}) - show-menu? (seq menu-entries) path (->> component (:path) (cpn/split-path) (cpn/join-path-with-dot))] (when (seq shapes) - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} - (if swap-opened? - [:button {:class (stl/css :title-back) + [:div {:class (stl/css :component-section)} + [:div {:class (stl/css :component-title)} + (if is-swap-opened + [:button {:class (stl/css :component-title-swap) :on-click on-component-back} [:> icon* {:icon-id i/arrow-left :size "s"}] @@ -941,9 +1002,9 @@ :collapsed (not open?) :on-collapsed toggle-content :title (tr "workspace.options.component") - :class (stl/css :title-spacing-component) - :title-class (stl/css :title-bar-variant)} - [:span {:class (stl/css :copy-text)} + :class (stl/css :component-title-bar) + :title-class (stl/css :component-title-bar-title)} + [:span {:class (stl/css :component-title-bar-type)} (if main-instance? (if is-variant? (tr "labels.variant") @@ -963,82 +1024,56 @@ :icon i/variant}])])] (when open? - [:div {:class (stl/css :element-content)} - [:div {:class (stl/css :component-line)} - - [:div {:class (stl/css :component-wrapper)} - - [:button {:class (stl/css-case :component-name-wrapper true - :without-menu (not show-menu?)) - :data-testid "swap-component-btn" - :on-click open-component-panel - :disabled (or swap-opened? (not can-swap?))} - - [:div {:class (stl/css :component-icon)} - [:> icon* {:size "s" - :icon-id (if main-instance? - (if is-variant? i/variant i/component) - i/component-copy)}]] - - [:div {:class (stl/css :component-name-outside)} - [:div {:class (stl/css :component-name)} - [:span {:class (stl/css :component-name-inside)} - (if (and multi (not same-variant?)) - (tr "settings.multiple") - (cpn/last-path shape-name))]] - - (when (and can-swap? (or (not multi) same-variant?)) - [:div {:class (stl/css :component-parent-name)} - (if (:deleted component) - (tr "workspace.options.component.unlinked") - (cpn/merge-path-item-with-dot path (:name component)))])]] - - (when show-menu? - [:div {:class (stl/css :component-actions)} - [:button {:class (stl/css-case :component-menu-btn true - :selected menu-open?) - :on-click on-menu-click} - [:> icon* {:icon-id i/menu}]] - - [:> component-ctx-menu* {:show menu-open? - :on-close on-menu-close - :menu-entries menu-entries - :main-instance main-instance?}]])] - + [:div {:class (stl/css :component-content)} + [:div {:class (stl/css :component-pill)} + [:> component-pill* {:icon (if main-instance? + (if is-variant? i/variant i/component) + i/component-copy) + :text (if (and multi (not same-variant?)) + (tr "settings.multiple") + (cpn/last-path shape-name)) + :subtext (when (and can-swap? (or (not multi) same-variant?)) + (if (:deleted component) + (tr "workspace.options.component.unlinked") + (cpn/merge-path-item-with-dot path (:name component)))) + :on-click open-component-panel + :disabled (or is-swap-opened (not can-swap?)) + :menu-entries menu-entries}] (when (and is-variant? main-instance?) [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant-property") :on-click add-new-property :icon i/add}])] - (when swap-opened? + (when is-swap-opened [:> component-swap* {:shapes copies}]) (when (and is-variant? (not main-instance?) (not (:deleted component)) - (not swap-opened?) + (not is-swap-opened) (or (not multi) same-variant?)) [:> component-variant-copy* {:current-file-id current-file-id :components components :shapes shapes :component-file-data data}]) - (when (and is-variant? main-instance? same-variant? (not swap-opened?)) - [:> component-variant-main-instance* {:components components - :shapes shapes - :data data}]) + (when (and is-variant? main-instance? same-variant? (not is-swap-opened)) + [:> component-variant* {:components components + :shapes shapes + :data data}]) - (when (and (not swap-opened?) (not multi)) + (when (and (not is-swap-opened) (not multi)) [:> component-annotation* {:id id :shape shape :component component :rerender-fn rerender-fn}]) (when (and multi all-main? (not any-variant?)) - [:button {:class (stl/css :combine-variant-button) - :on-click on-combine-as-variants} - [:span (tr "workspace.shape.menu.combine-as-variants")]]) + [:> button* {:variant "secondary" + :class (stl/css :component-combine) + :on-click on-combine-as-variants} + (tr "workspace.shape.menu.combine-as-variants")]) (when (dbg/enabled? :display-touched) [:div ":touched " (str (:touched shape))])])]))) @@ -1050,7 +1085,50 @@ (into (remove empty?) v) (into (filter empty?) v))) -(mf/defc variant-menu* +(mf/defc component-variant-main-property* + [{:keys [pos property is-remove-disabled on-remove on-blur on-reorder]}] + (let [values (->> (:value property) + (move-empty-items-to-end) + (replace {"" "--"}) + (str/join ", ")) + + on-drop + (mf/use-fn + (fn [relative-pos data] + (let [from-pos (:from-pos data) + to-space-between-pos (if (= relative-pos :bot) (inc pos) pos)] + (on-reorder from-pos to-space-between-pos)))) + + [dprops dref] + (h/use-sortable + :data-type "penpot/variant-main-property" + :on-drop on-drop + :draggable? true + :data {:from-pos pos})] + + [:div {:class (stl/css-case :variant-property true + :dnd-over-top (= (:over dprops) :top) + :dnd-over-bot (= (:over dprops) :bot))} + (when (some? on-reorder) + [:> reorder-handler* {:ref dref}]) + + [:div {:class (stl/css :variant-property-row)} + [:> input-with-meta* {:value (:name property) + :data-position pos + :meta values + :is-editing (:editing? (meta property)) + :max-length ctv/property-max-length + :on-blur on-blur}] + [:> icon-button* {:variant "ghost" + :icon i/remove + :data-position pos + :aria-label (if is-remove-disabled + (tr "workspace.shape.menu.remove-variant-property.last-property") + (tr "workspace.shape.menu.remove-variant-property")) + :on-click on-remove + :disabled is-remove-disabled}]]])) + +(mf/defc component-variant-main* [{:keys [shapes]}] (let [multi? (> (count shapes) 1) @@ -1080,13 +1158,11 @@ properties (mf/with-memo [data objects variant-id] (cfv/extract-properties-values data objects (:id shape))) + single-property? (= (count properties) 1) open* (mf/use-state true) open? (deref open*) - menu-open* (mf/use-state false) - menu-open? (deref menu-open*) - show-in-assets-panel (mf/use-fn (mf/deps variants) @@ -1119,19 +1195,6 @@ (mf/use-fn #(swap! open* not)) - on-menu-click - (mf/use-fn - (mf/deps menu-open* menu-open?) - (fn [event] - (dom/prevent-default event) - (dom/stop-propagation event) - (reset! menu-open* (not menu-open?)))) - - on-menu-close - (mf/use-fn - (mf/deps menu-open*) - #(reset! menu-open* false)) - on-click-variant-title-help (mf/use-fn (fn [] @@ -1162,6 +1225,12 @@ (ev/event {::ev/name "variant-remove-property" ::ev/origin "workspace:button-design-tab"}) (dwv/remove-property variant-id pos)))))) + reorder-properties + (mf/use-fn + (mf/deps variant-id) + (fn [from-pos to-space-between-pos] + (st/emit! (dwv/reorder-variant-poperties variant-id from-pos to-space-between-pos)))) + select-shapes-with-malformed (mf/use-fn (mf/deps malformed-ids) @@ -1173,95 +1242,56 @@ #(st/emit! (dw/select-shapes (into (d/ordered-set) duplicated-ids))))] (when (seq shapes) - [:div {:class (stl/css :element-set)} - [:div {:class (stl/css :element-title)} + [:div {:class (stl/css :component-section)} + [:div {:class (stl/css :component-title)} [:* [:> title-bar* {:collapsable true :collapsed (not open?) :on-collapsed toggle-content :title (tr "workspace.options.component") - :class (stl/css :title-spacing-component) - :title-class (stl/css :title-bar-variant)} - [:span {:class (stl/css :copy-text)} + :class (stl/css :component-title-bar) + :title-class (stl/css :component-title-bar-title)} + [:span {:class (stl/css :component-title-bar-type)} (tr "workspace.options.component.main")]] - [:div {:class (stl/css :title-actions)} - [:> icon-button* {:variant "ghost" - :aria-label (tr "workspace.options.component.variants-help-modal.title") - :on-click on-click-variant-title-help - :icon i/help}] - [:> icon-button* {:variant "ghost" - :aria-label (tr "workspace.shape.menu.add-variant") - :on-click (partial create-variant "workspace:button-design-tab-component") - :icon i/variant}]]]] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.options.component.variants-help-modal.title") + :on-click on-click-variant-title-help + :icon i/help}] + [:> icon-button* {:variant "ghost" + :aria-label (tr "workspace.shape.menu.add-variant") + :on-click (partial create-variant "workspace:button-design-tab-component") + :icon i/variant}]]] (when open? - [:div {:class (stl/css :element-content)} - [:div {:class (stl/css :component-line)} - - [:div {:class (stl/css :component-wrapper)} - - [:button {:class (stl/css :component-name-wrapper) - :disabled true} - - [:div {:class (stl/css :component-icon)} - [:> icon* {:size "s" - :icon-id i/component}]] - - [:div {:class (stl/css :component-name-outside)} - [:div {:class (stl/css :component-name)} - [:span {:class (stl/css :component-name-inside)} - (if multi? - (tr "settings.multiple") - (cpn/last-path shape-name))]]]] - - (when-not multi? - [:div {:class (stl/css :component-actions)} - - [:button {:class (stl/css-case :component-menu-btn true - :selected menu-open?) - :on-click on-menu-click} - [:> icon* {:icon-id i/menu}]] - - [:> component-ctx-menu* {:show menu-open? - :on-close on-menu-close - :menu-entries menu-entries - :main-instance true}]])] - + [:div {:class (stl/css :component-content)} + [:div {:class (stl/css :component-pill)} + [:> component-pill* {:icon i/component + :text (if multi? + (tr "settings.multiple") + (cpn/last-path shape-name)) + :disabled true + :menu-entries menu-entries}] [:> icon-button* {:variant "ghost" :aria-label (tr "workspace.shape.menu.add-variant-property") :on-click (partial add-new-property "workspace:button-design-tab-component") :icon i/add}]] (when-not multi? - [:div {:class (stl/css :variant-property-list)} - (for [[pos property] (map-indexed vector properties)] - (let [last-prop? (<= (count properties) 1) - values (->> (:value property) - (move-empty-items-to-end) - (replace {"" "--"}) - (str/join ", ")) - is-editing (:editing? (meta property))] - [:div {:key (str (:id shape) pos) - :class (stl/css :variant-property-row)} - [:> input-with-meta* {:value (:name property) - :meta values - :is-editing is-editing - :max-length ctv/property-max-length - :data-position pos - :on-blur update-property-name}] - [:> icon-button* {:variant "ghost" - :aria-label (if last-prop? - (tr "workspace.shape.menu.remove-variant-property.last-property") - (tr "workspace.shape.menu.remove-variant-property")) - :on-click remove-property - :data-position pos - :icon i/remove - :disabled last-prop?}]]))]) + [:> h/sortable-container* {} + [:div {:class (stl/css :variant-property-list)} + (for [[pos property] (map-indexed vector properties)] + [:> component-variant-main-property* {:key (str (:id shape) pos) + :pos pos + :property property + :is-remove-disabled single-property? + :on-remove remove-property + :on-blur update-property-name + :on-reorder reorder-properties}])]]) (if malformed? - [:div {:class (stl/css :variant-warning-wrapper)} + [:div {:class (stl/css :variant-warning)} [:> icon* {:icon-id i/msg-neutral :class (stl/css :variant-warning-darken)}] [:div {:class (stl/css :variant-warning-highlight)} @@ -1271,7 +1301,7 @@ (tr "workspace.options.component.variant.malformed.group.locate")]] (when duplicated? - [:div {:class (stl/css :variant-warning-wrapper)} + [:div {:class (stl/css :variant-warning)} [:> icon* {:icon-id i/msg-neutral :class (stl/css :variant-warning-darken)}] [:div {:class (stl/css :variant-warning-highlight)} 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 2da6699ac3..35a499746a 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 @@ -4,655 +4,617 @@ // // Copyright (c) KALEIDOS INC +@use "ds/_borders.scss" as *; +@use "ds/_sizes.scss" as *; +@use "ds/_utils.scss" as *; +@use "ds/spacing.scss" as *; @use "ds/typography.scss" as t; + @use "refactor/common-refactor.scss" as deprecated; -.element-set { - margin: 0; - display: grid; - grid-template-columns: repeat(8, var(--sp-xxxl)); - column-gap: var(--sp-xs); -} - -.element-content { - @include deprecated.flexColumn; - display: grid; - grid-template-columns: repeat(8, var(--sp-xxxl)); - row-gap: var(--sp-m); - padding-top: deprecated.$s-4; - padding-bottom: deprecated.$s-8; -} - -.element-title { +.annotation { + @include t.use-typography("body-small"); grid-column: span 8; - column-gap: var(--sp-xs); - display: flex; -} + color: var(--color-foreground-secondary); + border-radius: $br-8; -.title-back { - @include deprecated.uppercaseTitleTipography; - display: flex; - align-items: center; - gap: deprecated.$s-4; - width: 100%; - height: deprecated.$s-32; - padding: 0; - border: 0; - border-radius: deprecated.$br-8; - background-color: var(--title-background-color); - color: var(--title-foreground-color); - cursor: pointer; -} + &.editing { + border: $b-1 solid var(--color-accent-primary); -.title-spacing-component { - justify-content: flex-start; - gap: deprecated.$s-8; - flex-grow: 1; -} + .annotation-title { + border-bottom: $b-1 solid var(--entry-border-color-disabled); + } -.title-bar-variant { - flex-grow: 0; -} + .annotation-title-icon-action { + display: flex; + } -.title-actions { - display: flex; - gap: var(--sp-xs); -} + .annotation-title-name { + margin-inline-start: var(--sp-m); + } -.component-line { - grid-column: span 8; - width: 100%; - min-height: deprecated.$s-32; - border-radius: deprecated.$br-8; - display: flex; - gap: var(--sp-xs); -} - -.component-wrapper { - display: flex; - flex-grow: 1; - gap: deprecated.$s-1; -} - -.component-name-wrapper { - @include deprecated.buttonStyle; - cursor: default; - flex-grow: 1; - display: grid; - grid-template-columns: deprecated.$s-16 1fr; - gap: deprecated.$s-4; - padding: 0 deprecated.$s-8; - border-radius: deprecated.$br-8 0 0 deprecated.$br-8; - background-color: var(--assets-item-background-color); - color: var(--assets-item-name-foreground-color-hover); - - &.without-menu { - width: 100%; - border-radius: deprecated.$br-8; + .annotation-textarea { + min-block-size: $sz-252; + } } - &:focus { - outline: deprecated.$s-1 solid var(--color-accent-primary); + &.creating { + border: $b-1 solid var(--color-accent-primary); + + .annotation-title .annotation-title-icon-action { + display: flex; + } + + .annotation-title-name { + margin-inline-start: var(--sp-m); + } + + .annotation-textarea { + min-block-size: $sz-252; + } + } +} + +.annotation-title { + display: flex; + align-items: center; + block-size: $sz-32; + + &.expanded { + border-bottom: $b-1 solid var(--color-background-quaternary); + } + + &.expandeable { + cursor: pointer; + } + + &:hover { + .annotation-title-icon-action { + display: flex; + } + } +} + +.annotation-title-name { + flex-grow: 1; +} + +.annotation-title-icon-arrow { + margin: 0 var(--sp-xs); +} + +.annotation-title-actions { + display: flex; + align-items: center; + line-height: 2.5; +} + +.annotation-title-icon-action { + cursor: pointer; + display: none; + justify-content: center; + align-items: center; + inline-size: $sz-28; + block-size: $sz-32; + border-radius: $br-8; + margin: 0; + padding: 0; +} + +.annotation-title-icon-ok:hover { + color: var(--color-accent-success); + + &.disabled { + cursor: default; + color: var(--color-foreground-secondary); + } +} + +.annotation-title-icon-nok:hover { + color: var(--color-accent-error); +} + +.annotation-body-hidden { + display: none; +} + +// Auto growing text +.annotation-body { + // 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; + + /* Identical styling required!! */ + font: inherit; + overflow-wrap: anywhere; + + padding: var(--sp-m); + + /* Place on top of each other */ + grid-area: 1 / 1 / 2 / 2; + } +} + +.annotation-textarea { + background-color: var(--color-background-primary); + color: var(--color-foreground-primary); + padding: var(--sp-m); + + border: none; + overflow: hidden; + outline: none; + + box-shadow: none; + + resize: none; + + /* Identical styling required!! */ + font: inherit; + overflow-wrap: anywhere; + + padding: var(--sp-m); + + /* Place on top of each other */ + grid-area: 1 / 1 / 2 / 2; +} + +.annotation-counter { + @include t.use-typography("body-small"); + text-align: right; + color: var(--color-foreground-secondary); + margin: 0 var(--sp-s) var(--sp-s) 0; +} + +.swap-item-list { + --swap-item-foreground-color: var(--color-foreground-primary); + --swap-item-foreground-color-hover: var(--color-foreground-primary); + --swap-item-foreground-color-disabled: var(--color-foreground-disabled); + --swap-item-background-color: var(--color-background-tertiary); + --swap-item-background-color-hover: var(--color-background-quaternary); + --swap-item-border-color: transparent; + --swap-item-border-color-selected: var(--color-accent-primary); + --swap-item-thumbnail-background-color: var(--color-canvas); + + @include t.use-typography("body-small"); + display: flex; + align-items: center; + padding: px2rem(1) var(--sp-m) px2rem(1) px2rem(1); + gap: var(--sp-s); + block-size: calc($sz-32 + $sz-12); + border-radius: $br-8; + background-color: var(--swap-item-background-color); + color: var(--swap-item-foreground-color); + border: $b-1 solid var(--swap-item-border-color); + + .swap-item-thumbnail { + flex: 0 0 $sz-40; + background-color: var(--swap-item-thumbnail-background-color); + border-radius: $br-6; + block-size: $sz-40; + inline-size: $sz-40; + padding: var(--sp-xxs); + } + + .swap-item-name { + flex: 1; + text-align: left; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .swap-item-variant-icon { + flex: 0 0 $sz-16; + background-color: none; + } + + &.selected { + --swap-item-border-color: var(--swap-item-border-color-selected); } &:hover:not(:disabled) { - background-color: var(--assets-item-background-color-hover); - color: var(--assets-item-name-foreground-color-hover); + --swap-item-foreground-color: var(--swap-item-foreground-color-hover); + --swap-item-background-color: var(--swap-item-background-color-hover); + } + + &:disabled { + --swap-item-foreground-color: var(--swap-item-foreground-color-disabled); } } -.component-icon { +.swap-item-grid { + --swap-item-foreground-color: var(--color-foreground-primary); + --swap-item-foreground-color-hover: var(--color-foreground-primary); + --swap-item-background-color: var(--color-background-primary); + --swap-item-border-color: var(--color-accent-primary); + --swap-item-border-inner-color-selected: var(--color-background-primary); + --swap-item-thumbnail-background-color: var(--color-canvas); + --swap-item-thumbnail-background-color-disabled: var(--color-foreground-secondary); + display: flex; - height: deprecated.$s-32; + justify-content: center; align-items: center; - color: var(--icon-foreground); + place-items: center; + aspect-ratio: 1 / 1; + flex-wrap: wrap; + position: relative; + padding: var(--sp-s); + border: none; + border-radius: $br-8; + background-color: var(--swap-item-thumbnail-background-color); + overflow: hidden; + + .swap-item-thumbnail { + block-size: 100%; + inline-size: 100%; + object-fit: contain; + } + + .swap-item-name { + @include t.use-typography("body-small"); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + text-align: left; + display: none; + position: absolute; + inset-inline-start: var(--sp-xs); + inset-block-end: var(--sp-xs); + block-size: var(--sp-xxl); + inline-size: calc(100% - var(--sp-s)); + padding: var(--sp-xs); + column-gap: var(--sp-xs); + border-radius: $br-4; + border: $b-1 solid transparent; + background-color: var(--swap-item-background-color); + color: var(--swap-item-foreground-color); + direction: rtl; + } + + .swap-item-variant-icon { + position: absolute; + inset-inline-end: var(--sp-xxs); + inset-block-start: var(--sp-xxs); + background-color: var(--color-background-tertiary); + } + + &:hover { + .swap-item-name { + display: block; + } + } + + &.selected { + border: $b-2 solid var(--swap-item-border-color); + + &::before { + content: " "; + position: absolute; + inset-inline-start: 0; + inset-inline-end: 0; + inset-block-start: 0; + inset-block-end: 0; + border: calc($b-2 * 2) solid var(--swap-item-border-inner-color-selected); + border-radius: $br-8; + } + + .swap-item-name { + color: var(--swap-item-foreground-color-hover); + } + } + + &:disabled { + background: var(--swap-item-thumbnail-background-color-disabled); + + .swap-item-name { + background: linear-gradient( + to top, + var(--swap-item-thumbnail-background-color-disabled) 0%, + transparent 100% + ); + color: var(--swap-item-foreground-color-hover); + } + } } -.component-name-outside { - @include deprecated.flexColumn; - min-height: deprecated.$s-32; - padding: deprecated.$s-8 0 deprecated.$s-8 deprecated.$s-2; - border-radius: deprecated.$br-8 0 0 deprecated.$br-8; +.swap-item-variant-icon { + display: flex; + justify-content: center; + align-items: center; + block-size: $sz-24; + inline-size: $sz-24; + color: var(--color-accent-secondary); + border-radius: $br-8; +} + +.swap-group { + @include t.use-typography("body-small"); + cursor: pointer; + display: grid; + grid-template-columns: 1fr var(--sp-m); + block-size: $sz-32; + align-items: center; + + &:hover { + .swap-group-icon { + color: var(--color-foreground-primary); + } + } +} + +.swap-group-name { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--color-foreground-primary); +} + +.swap-group-icon { + color: var(--color-foreground-secondary); +} + +.swap { + padding-block-start: var(--sp-m); + grid-column: span 8; +} + +.swap-title { + @include t.use-typography("headline-small"); + display: flex; + align-items: center; + block-size: $sz-32; + padding-inline-start: var(--sp-xxs); + color: var(--color-foreground-secondary); +} + +.swap-content { + display: flex; + flex-direction: column; + gap: var(--sp-l); +} + +.swap-filters { + display: flex; + flex-direction: column; + gap: var(--sp-xs); +} + +.swap-library { + display: flex; + flex-direction: column; + gap: var(--sp-xs); +} + +.swap-library-title { + display: grid; + grid-template-columns: 1fr auto; +} + +.swap-library-name { + @include t.use-typography("body-small"); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--color-foreground-secondary); + padding: var(--sp-s) 0 var(--sp-s) var(--sp-xxs); +} + +.swap-library-back { + cursor: pointer; + display: flex; + align-items: center; + gap: var(--sp-xs); + inline-size: 100%; + block-size: $sz-32; + padding: 0; + border: 0; + background-color: var(--color-background-primary); + color: var(--color-foreground-secondary); +} + +.swap-library-back-name { + @include t.use-typography("body-small"); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + direction: rtl; + block-size: $sz-32; + padding: var(--sp-s) 0 var(--sp-s) var(--sp-xxs); +} + +.swap-library-empty { + @include t.use-typography("body-small"); + margin: 0 var(--sp-xs) 0 var(--sp-s); + color: var(--color-foreground-secondary); +} + +.swap-library-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(calc($sz-48 * 2), 1fr)); + gap: var(--sp-xs); +} + +.swap-library-list { + display: flex; + flex-direction: column; + gap: var(--sp-xs); +} + +.component-section { + display: grid; + grid-template-columns: repeat(8, var(--sp-xxxl)); + column-gap: var(--sp-xs); +} + +.component-title { + grid-column: span 8; + column-gap: var(--sp-xs); + display: flex; +} + +.component-title-swap { + @include t.use-typography("headline-small"); + cursor: pointer; + display: flex; + align-items: center; + gap: var(--sp-xs); + inline-size: 100%; + block-size: $sz-32; + padding: 0; + border: 0; + border-radius: $br-8; + background-color: var(--color-background-primary); + color: var(--color-foreground-secondary); +} + +.component-title-bar { + justify-content: flex-start; + gap: var(--sp-s); + flex-grow: 1; +} + +.component-title-bar-title { + flex-grow: 0; +} + +.component-title-bar-type { + @include t.use-typography("body-small"); + block-size: 100%; + display: flex; + align-items: center; + color: var(--color-foreground-secondary); +} + +.component-title-actions { + display: flex; + gap: var(--sp-xs); +} + +.component-content { + grid-column: span 8; + display: flex; + flex-direction: column; + row-gap: var(--sp-m); + padding-block-start: var(--sp-xs); + padding-block-end: var(--sp-s); +} + +.component-pill { + border-radius: $br-8; + display: flex; + gap: var(--sp-xs); +} + +.component-combine { + justify-content: center; +} + +.pill { + display: flex; + flex-grow: 1; + gap: px2rem(1); +} + +.pill-btn { + flex-grow: 1; + display: grid; + grid-template-columns: var(--sp-l) 1fr; + gap: var(--sp-xs); + padding: 0 var(--sp-m) 0 var(--sp-s); + border: none; + border-radius: $br-8; + background-color: var(--color-background-tertiary); + color: var(--color-foreground-primary); + + &.with-menu { + border-radius: $br-8 0 0 $br-8; + } + + &:focus { + outline: $b-1 solid var(--color-accent-primary); + } + + &:hover:not(:disabled) { + background-color: var(--color-background-quaternary); + color: var(--color-foreground-primary); + } +} + +.pill-btn-icon { + display: flex; + block-size: $sz-32; + align-items: center; + color: var(--color-foreground-secondary); +} + +.pill-btn-name { + display: flex; + flex-direction: column; + min-block-size: $sz-32; + padding: var(--sp-s) 0 var(--sp-s) var(--sp-xxs); + border-radius: $br-8 0 0 $br-8; overflow: hidden; gap: 0; } -.component-name { - @include deprecated.bodySmallTypography; - @include deprecated.textEllipsis; +.pill-btn-text { + @include t.use-typography("body-small"); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; direction: rtl; text-align: left; - height: deprecated.$s-16; + block-size: $sz-16; } -.component-name-inside { - direction: ltr; - unicode-bidi: bidi-override; -} - -.component-parent-name { - @include deprecated.bodySmallTypography; - @include deprecated.textEllipsis; +.pill-btn-subtext { + @include t.use-typography("body-small"); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; direction: rtl; text-align: left; - height: deprecated.$s-16; - max-width: deprecated.$s-184; - color: var(--title-foreground-color); + block-size: $sz-16; + color: var(--color-foreground-secondary); } -.component-actions { +.pill-actions { + flex: 0 0 $sz-32; position: relative; - width: deprecated.$s-32; } -.component-menu-btn { +.pill-actions-btn { @extend .button-secondary; cursor: unset; - height: 100%; - width: 100%; - border-radius: 0 deprecated.$br-8 deprecated.$br-8 0; + block-size: 100%; + inline-size: 100%; + border-radius: 0 $br-8 $br-8 0; &.selected { @extend .button-icon-selected; } } -.copy-text { - @include deprecated.bodySmallTypography; - height: 100%; - display: flex; - align-items: center; - color: var(--title-foreground-color); -} - -.custom-select-dropdown { +.pill-actions-dropdown { @extend .dropdown-wrapper; - right: 0; - left: unset; - width: deprecated.$s-252; + inline-size: $sz-252; + inset-inline-end: 0; + inset-inline-start: unset; + + &.extended { + inset-block-start: $sz-48; + } } -.not-main { - top: deprecated.$s-48; -} - -.dropdown-element { +.pill-actions-dropdown-item { @extend .dropdown-element-base; } -.component-path { - display: flex; - align-items: center; - gap: deprecated.$s-4; - width: 100%; - height: deprecated.$s-32; - padding: 0; - border: 0; - background-color: var(--title-background-color); - color: var(--title-foreground-color); - cursor: pointer; -} - -.path-name { - @include deprecated.bodySmallTypography; - @include deprecated.textEllipsis; - direction: rtl; - height: deprecated.$s-32; - padding: deprecated.$s-8 0 deprecated.$s-8 deprecated.$s-2; - margin-right: deprecated.$s-4; -} - -.component-list-empty { - @include deprecated.bodySmallTypography; - margin: 0 deprecated.$s-4 0 deprecated.$s-8; - color: var(--color-foreground-secondary); -} - -.component-item { - display: flex; - align-items: center; - margin-bottom: deprecated.$s-4; - padding: deprecated.$s-1 deprecated.$s-8 deprecated.$s-1 deprecated.$s-1; - gap: deprecated.$s-8; - font-size: deprecated.$s-12; - cursor: pointer; - width: 100%; - height: deprecated.$s-44; - border-radius: deprecated.$br-8; - background-color: var(--assets-item-background-color); - color: var(--assets-item-name-foreground-color); - border: deprecated.$s-1 solid transparent; - - .variant-icon { - background-color: none; - padding: deprecated.$s-2; - flex: 0 0 deprecated.$s-16; - } - - .component-name { - @include deprecated.textEllipsis; - width: 80%; - } - - .component-img { - flex: 0 0 deprecated.$s-40; - background-color: var(--assets-component-background-color); - border-radius: deprecated.$br-6; - height: deprecated.$s-40; - width: deprecated.$s-40; - padding: deprecated.$s-2; - } - - &.selected { - border: deprecated.$s-1 solid var(--assets-item-border-color); - } - - &: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); - - svg { - cursor: auto; - } - } -} - -.component-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(deprecated.$s-96, 1fr)); - gap: deprecated.$s-4; -} - -.grid-cell { - @include deprecated.flexCenter; - place-items: center; - aspect-ratio: 1 / 1; - flex-wrap: wrap; - position: relative; - padding: deprecated.$s-8; - border-radius: deprecated.$br-8; - background-color: var(--assets-component-background-color); - overflow: hidden; - cursor: pointer; - - .variant-icon { - background-color: var(--color-background-tertiary); - } - - img { - height: auto; - width: auto; - max-height: 100%; - max-width: 100%; - pointer-events: none; - border: 0; - } - - .component-img { - height: 100%; - width: 100%; - object-fit: contain; - } - - .component-name { - @include deprecated.bodySmallTypography; - @include deprecated.textEllipsis; - display: none; - position: absolute; - left: deprecated.$s-4; - bottom: deprecated.$s-4; - height: calc(deprecated.$s-24 - deprecated.$s-2); - width: calc(100% - 2 * deprecated.$s-4); - padding: deprecated.$s-2 deprecated.$s-6; - column-gap: deprecated.$s-4; - border-radius: deprecated.$br-4; - background-color: var(--assets-item-name-background-color); - border: deprecated.$s-1 solid transparent; - color: var(--assets-item-name-foreground-color); - direction: rtl; - } - - &:hover { - .component-name { - display: block; - } - } - - &.selected { - border: deprecated.$s-2 solid var(--assets-item-border-color); - - &::before { - content: " "; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - border: deprecated.$s-4 solid var(--assets-component-second-border-selected); - border-radius: deprecated.$br-8; - } - - .component-name { - color: var(--assets-item-name-foreground-color-hover); - } - } - - &.disabled { - background: var(--assets-component-background-color-disabled); - cursor: auto; - - svg { - cursor: auto; - } - - .component-name { - background: linear-gradient( - to top, - var(--assets-component-background-color-disabled) 0%, - transparent 100% - ); - color: var(--assets-item-name-foreground-color-hover); - } - } -} - -.element-set-title { - @include deprecated.uppercaseTitleTipography; - display: flex; - align-items: center; - height: deprecated.$s-32; - padding-left: deprecated.$s-2; - color: var(--title-foreground-color); -} - -// Component swap - -.component-swap { - padding-top: deprecated.$s-12; - grid-column: span 8; -} - -.component-swap-content { - @include deprecated.flexColumn; - gap: deprecated.$s-16; -} - -.fields-wrapper { - @include deprecated.flexColumn; - gap: deprecated.$s-4; -} - -.search-field { - display: flex; - align-items: center; - height: deprecated.$s-32; - border-radius: deprecated.$br-8; - font-family: "worksans", "vazirmatn", sans-serif; - background-color: var(--input-background-color); -} - -.library-name-wrapper { - display: grid; - grid-template-columns: 1fr auto; -} - -.library-name { - @include deprecated.bodySmallTypography; - @include deprecated.textEllipsis; - color: var(--title-foreground-color); - padding: deprecated.$s-8 0 deprecated.$s-8 deprecated.$s-2; -} - -.swap-wrapper { - @include deprecated.flexColumn; - gap: deprecated.$s-4; -} - -.listing-options-wrapper { - width: 100%; -} - -.listing-options { - display: flex; - align-items: center; -} - -.component-group { - @include deprecated.bodySmallTypography; - display: grid; - grid-template-columns: 1fr deprecated.$s-12; - height: deprecated.$s-32; - cursor: pointer; - align-items: center; - - .component-group-name { - @include deprecated.textEllipsis; - color: var(--assets-item-name-foreground-color); - } - - &:hover { - color: var(--assets-item-name-foreground-color-hover); - - .component-group-name { - color: var(--assets-item-name-foreground-color-hover); - } - } -} - -.component-group-icon { - color: var(--icon-foreground); -} - -.path-wrapper { - display: flex; - max-width: deprecated.$s-232; - padding: deprecated.$s-8 0 deprecated.$s-8 deprecated.$s-2; -} - -.component-group-path { - @include deprecated.textEllipsis; - direction: rtl; - color: var(--assets-item-name-foreground-color-rest); -} - -// Component annotation - -.component-annotation { - @include deprecated.bodySmallTypography; - grid-column: span 8; - color: var(--entry-foreground-color); - border-radius: deprecated.$br-8; - - .annotation-title { - display: flex; - align-items: center; - height: deprecated.$s-32; - - &.expanded { - border-bottom: deprecated.$s-1 solid var(--entry-border-color-disabled); - } - - &.expandeable { - cursor: pointer; - } - - div { - display: flex; - align-items: center; - line-height: 2.5; - } - - .icon-arrow { - @include deprecated.flexCenter; - width: deprecated.$s-28; - height: deprecated.$s-32; - display: flex; - margin: 0; - padding: 0; - cursor: pointer; - - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - width: deprecated.$s-16; - height: deprecated.$s-16; - } - - &.expanded svg { - transform: rotate(90deg); - } - } - - .icon { - @include deprecated.flexCenter; - width: deprecated.$s-28; - height: deprecated.$s-32; - border-radius: deprecated.$br-8; - display: none; - margin: 0; - padding: 0; - cursor: pointer; - - svg { - @extend .button-icon; - stroke: var(--icon-foreground); - width: deprecated.$s-16; - height: deprecated.$s-16; - } - - &.icon-tick:hover, - &.icon-edit:hover { - svg { - stroke: var(--icon-foreground-accept); - } - } - - &.icon-tick.invalid:hover { - cursor: default; - - svg { - stroke: var(--icon-foreground); - } - } - - &.icon-cross:hover, - &.icon-trash:hover { - svg { - stroke: var(--icon-foreground-discard); - } - } - } - - .annotation-text { - flex-grow: 1; - margin-left: deprecated.$s-12; - } - - &:hover { - .icon { - display: flex; - } - } - } - - &.editing { - border: deprecated.$s-1 solid var(--input-border-color-success); - - .annotation-title { - border-bottom: deprecated.$s-1 solid var(--entry-border-color-disabled); - - .icon { - display: flex; - } - } - - textarea { - min-height: deprecated.$s-252; - } - } - - &.creating { - border: deprecated.$s-1 solid var(--input-border-color-success); - - .annotation-title .icon { - display: flex; - } - - textarea { - min-height: deprecated.$s-252; - } - } - - .hidden { - display: none; - - svg { - display: none; - } - } - - .counter { - @include deprecated.bodySmallTypography; - text-align: right; - color: var(--entry-foreground-color); - margin: 0 deprecated.$s-8 deprecated.$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: deprecated.$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; - } - } -} - -.variant-property-row { - @include deprecated.flexRow; - justify-content: space-between; - width: 100%; -} - .variant-property-list { grid-column: span 8; @@ -661,8 +623,34 @@ gap: var(--sp-xs); } +.variant-property { + display: flex; + flex-direction: column; + position: relative; + + --reorder-left-position: calc(-1 * var(--sp-m) - var(--sp-xxs)); + + &:hover { + --reorder-icon-visibility: visible; + } + + &.dnd-over-top { + --reorder-top-display: block; + } + + &.dnd-over-bot { + --reorder-bottom-display: block; + } +} + +.variant-property-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: var(--sp-xs); +} + .variant-property-container { - @include t.use-typography("body-small"); display: grid; grid-template-columns: repeat(8, var(--sp-xxxl)); gap: var(--sp-xs); @@ -679,6 +667,7 @@ } .variant-property-name { + @include t.use-typography("body-small"); color: var(--color-foreground-secondary); display: block; overflow: hidden; @@ -686,16 +675,16 @@ white-space: nowrap; } -.variant-warning-wrapper { - @include deprecated.bodySmallTypography; +.variant-warning { + @include t.use-typography("body-small"); grid-column: span 8; - border: 1px solid var(--color-background-quaternary); - border-radius: deprecated.$s-8; - padding: deprecated.$s-12; + border: $b-1 solid var(--color-background-quaternary); + border-radius: $br-8; + padding: var(--sp-m); display: flex; flex-direction: column; - gap: deprecated.$s-8; + gap: var(--sp-s); } .variant-warning-highlight { @@ -707,50 +696,12 @@ } .variant-warning-button { - @include deprecated.bodySmallTypography; + @include t.use-typography("body-small"); + cursor: pointer; background-color: transparent; border: none; appearance: none; color: var(--color-accent-primary); - cursor: pointer; padding: 0; text-align: start; } - -.variant-icon { - @include deprecated.flexCenter; - height: deprecated.$s-24; - width: deprecated.$s-24; - color: var(--color-accent-secondary); - border-radius: deprecated.$s-8; -} - -.variant-mark-cell { - position: absolute; - right: deprecated.$s-2; - top: deprecated.$s-2; -} - -.combine-variant-button { - @include deprecated.buttonStyle; - @include deprecated.uppercaseTitleTipography; - grid-column: span 8; - cursor: default; - display: flex; - justify-content: center; - align-items: center; - padding: deprecated.$s-8; - border-radius: deprecated.$br-8; - background-color: var(--assets-item-background-color); - color: var(--color-foreground-secondary); - cursor: pointer; - - &:hover { - background-color: var(--assets-item-background-color-hover); - color: var(--color-foreground-primary); - } - - &:focus { - outline: deprecated.$s-1 solid var(--color-accent-primary); - } -} diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs index a9d4d194aa..d39ce2cef2 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.cljs @@ -250,7 +250,7 @@ :icon i/remove}]] (some? fills) - [:& h/sortable-container {} + [:> h/sortable-container* {} (for [value fills] (let [mdata (meta value) index (get mdata :index) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs index 68a60566ba..bcc4f928ec 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/layout_container.cljs @@ -812,7 +812,7 @@ [:button {:class (stl/css :add-column) :on-click add-track} deprecated-icon/add]] (when expanded? - [:& h/sortable-container {} + [:> h/sortable-container* {} [:div {:class (stl/css :grid-tracks-info-container)} (for [[index column] (d/enumerate column-values)] [:& grid-track-info {:key (dm/str index "-" (d/name type)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs index 7f32564ac5..40feb597dc 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/shadow.cljs @@ -356,7 +356,7 @@ :icon i/remove}]]]] (some? shadows) - [:& h/sortable-container {} + [:> h/sortable-container* {} [:div {:class (stl/css :element-set-content)} (for [{:keys [::index id] :as shadow} shadows] [:> shadow-entry* diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs index a7b2403333..91b140011b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs @@ -203,7 +203,7 @@ :on-click handle-remove-all :icon i/remove}]] (seq strokes) - [:& h/sortable-container {} + [:> h/sortable-container* {} (for [[index value] (d/enumerate (:strokes values []))] [:& stroke-row {:key (dm/str "stroke-" index) :stroke value diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs index dec42cb3da..e3103f2642 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/frame.cljs @@ -12,7 +12,7 @@ [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]] - [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu variant-menu*]] + [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu* component-variant-main*]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-menu* exports-attrs]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] @@ -107,10 +107,10 @@ :type shape-type :shapes shapes}] - [:& component-menu {:shapes shapes}] + [:> component-menu* {:shapes shapes}] (when is-variant? - [:> variant-menu* {:shapes shapes}]) + [:> component-variant-main* {:shapes shapes}]) [:& layout-container-menu {:type shape-type diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 4887cdc324..efaaf22b0b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -22,7 +22,7 @@ [app.main.refs :as refs] [app.main.ui.workspace.sidebar.options.menus.blur :refer [blur-attrs blur-menu]] [app.main.ui.workspace.sidebar.options.menus.color-selection :refer [color-selection-menu*]] - [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu]] + [app.main.ui.workspace.sidebar.options.menus.component :refer [component-menu*]] [app.main.ui.workspace.sidebar.options.menus.constraints :refer [constraint-attrs constraints-menu]] [app.main.ui.workspace.sidebar.options.menus.exports :refer [exports-attrs exports-menu*]] [app.main.ui.workspace.sidebar.options.menus.fill :as fill] @@ -234,7 +234,7 @@ merge-token-values (fn [acc shape-attrs applied-tokens] - "Merges token values across all shape attributes. + "Merges token values across all shape attributes. For each shape attribute, its corresponding token attributes are merged into the accumulator. If applied tokens are empty, the accumulator is returned unchanged." (if (seq applied-tokens) @@ -455,7 +455,7 @@ :shapes shapes}]) (when (some? components) - [:& component-menu {:shapes components}]) + [:> component-menu* {:shapes components}]) [:& layout-container-menu {:type type diff --git a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs index 57d8fa007b..cce3eb7d63 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/sitemap.cljs @@ -202,7 +202,7 @@ editing-page-id (mf/deref refs/editing-page-item) current-page-id (mf/use-ctx ctx/current-page-id)] [:ul {:class (stl/css :page-list)} - [:& hooks/sortable-container {} + [:> hooks/sortable-container* {} (for [[index page-id] (d/enumerate pages)] [:& page-item-wrapper {:page-id page-id diff --git a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs index 401324ea99..750049166b 100644 --- a/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs +++ b/frontend/src/app/main/ui/workspace/tokens/sidebar.cljs @@ -51,7 +51,7 @@ (when-not token-set-new-path [:> tsetslist/inline-add-button*]) - [:> h/sortable-container {} + [:> h/sortable-container* {} [:> tsets/sets-list* {:tokens-lib tokens-lib :new-path token-set-new-path