diff --git a/frontend/resources/images/icons/easing-ease-in-out-refactor.svg b/frontend/resources/images/icons/easing-ease-in-out-refactor.svg new file mode 100644 index 0000000000..def35ce0e5 --- /dev/null +++ b/frontend/resources/images/icons/easing-ease-in-out-refactor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/resources/images/icons/easing-ease-in-refactor.svg b/frontend/resources/images/icons/easing-ease-in-refactor.svg new file mode 100644 index 0000000000..8d2d215ce6 --- /dev/null +++ b/frontend/resources/images/icons/easing-ease-in-refactor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/resources/images/icons/easing-ease-out-refactor.svg b/frontend/resources/images/icons/easing-ease-out-refactor.svg new file mode 100644 index 0000000000..03209db2d3 --- /dev/null +++ b/frontend/resources/images/icons/easing-ease-out-refactor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/resources/images/icons/easing-ease-refactor.svg b/frontend/resources/images/icons/easing-ease-refactor.svg new file mode 100644 index 0000000000..68cac1fa61 --- /dev/null +++ b/frontend/resources/images/icons/easing-ease-refactor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/resources/images/icons/easing-linear-refactor.svg b/frontend/resources/images/icons/easing-linear-refactor.svg new file mode 100644 index 0000000000..2890f2212e --- /dev/null +++ b/frontend/resources/images/icons/easing-linear-refactor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/resources/images/icons/interaction-refactor.svg b/frontend/resources/images/icons/interaction-refactor.svg new file mode 100644 index 0000000000..7c3fb8462a --- /dev/null +++ b/frontend/resources/images/icons/interaction-refactor.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/resources/styles/common/refactor/basic-rules.scss b/frontend/resources/styles/common/refactor/basic-rules.scss index 0206395eaf..8b912412c7 100644 --- a/frontend/resources/styles/common/refactor/basic-rules.scss +++ b/frontend/resources/styles/common/refactor/basic-rules.scss @@ -375,6 +375,37 @@ } } +.input-checkbox { + display: flex; + align-items: center; + label { + @include titleTipography; + display: flex; + align-items: center; + gap: $s-6; + cursor: pointer; + span { + @include flexCenter; + width: $s-16; + height: $s-16; + min-width: $s-16; + min-height: $s-16; + border-radius: $br-6; + background-color: var(--input-background-color); + &:global(.checked) { + background-color: var(--input-border-color-active); + svg { + @extend .button-icon-small; + stroke: var(--input-details-color); + } + } + } + input { + margin: 0; + } + } +} + //MODALS .modal-background { @include menuShadow; @@ -467,8 +498,14 @@ border-color: var(--colorpicker-details-color-selected); } } -// SELECTS AND DROPDOWNS +.attr-row { + display: grid; + grid-template-areas: "name content"; + grid-template-columns: $s-92 1fr; +} + +// SELECTS AND DROPDOWNS .menu-dropdown { @include menuShadow; position: absolute; diff --git a/frontend/resources/styles/common/refactor/design-tokens.scss b/frontend/resources/styles/common/refactor/design-tokens.scss index aec6339a37..29d7670159 100644 --- a/frontend/resources/styles/common/refactor/design-tokens.scss +++ b/frontend/resources/styles/common/refactor/design-tokens.scss @@ -14,6 +14,8 @@ --panel-background-color: var(--color-background-primary); --panel-title-background-color: var(--color-background-secondary); + // BUTTONS + --button-foreground-hover: var(--color-accent-primary); --button-background-focus: var(--color-background-secondary); --button-foreground-focus: var(--color-foreground-primary); @@ -84,6 +86,7 @@ --constraint-widget-background-color: var(--color-background-tertiary); --constraint-center-area-background-color: var(--color-background-primary); + // ICONS --icon-foreground: var(--color-foreground-secondary); --icon-foreground-hover: var(--color-foreground-primary); diff --git a/frontend/src/app/main/ui/components/select.cljs b/frontend/src/app/main/ui/components/select.cljs index 04a963d3b1..2fabc9d226 100644 --- a/frontend/src/app/main/ui/components/select.cljs +++ b/frontend/src/app/main/ui/components/select.cljs @@ -19,8 +19,8 @@ (defn- as-key-value [item] (if (map? item) - [(:value item) (:label item)] - [item item])) + [(:value item) (:label item) (:icon item)] + [item item item])) (mf/defc select [{:keys [default-value options class is-open? on-change on-pointer-enter-option on-pointer-leave-option disabled]}] @@ -42,7 +42,7 @@ open-dropdown (mf/use-fn (mf/deps disabled) - (fn[] + (fn [] (when-not disabled (swap! state* assoc :is-open? true)))) @@ -82,10 +82,16 @@ (mf/with-effect [default-value] (swap! state* assoc :current-value default-value)) + (if new-css-system [:div {:on-click open-dropdown :class (dm/str class " " (stl/css-case :custom-select true :disabled disabled))} + (let [selected-option (first (filter #(= (:value %) default-value) options)) + current-icon (:icon selected-option) + current-icon-ref (i/key->icon current-icon)] + (when (and current-icon current-icon-ref) + [:span {:class (stl/css :current-icon)} @current-icon-ref])) [:span {:class (stl/css :current-label)} current-label] [:span {:class (stl/css :dropdown-button)} i/arrow-refactor] [:& dropdown {:show is-open? :on-close close-dropdown} @@ -94,7 +100,8 @@ (if (= :separator item) [:li {:class (dom/classnames (stl/css :separator) true) :key (dm/str current-id "-" index)}] - (let [[value label] (as-key-value item)] + (let [[value label icon] (as-key-value item) + icon-ref (i/key->icon icon)] [:li {:key (dm/str current-id "-" index) :class (dom/classnames @@ -104,10 +111,10 @@ :on-pointer-enter highlight-item :on-pointer-leave unhighlight-item :on-click select-item} + (when (and icon icon-ref) [:span {:class (stl/css :icon)} @icon-ref]) [:span {:class (stl/css :label)} label] [:span {:class (stl/css :check-icon)} i/tick-refactor]])))]]] - [:div.custom-select {:on-click open-dropdown :class class} [:span current-label] [:span.dropdown-button i/arrow-down] diff --git a/frontend/src/app/main/ui/components/select.scss b/frontend/src/app/main/ui/components/select.scss index 7fb9fcd37b..8b4625d79e 100644 --- a/frontend/src/app/main/ui/components/select.scss +++ b/frontend/src/app/main/ui/components/select.scss @@ -24,6 +24,16 @@ width: 100%; flex-grow: 1; } + .current-icon { + @include flexCenter; + height: $s-24; + width: $s-24; + padding-right: $s-4; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + } .dropdown-button { @include flexCenter; svg { @@ -42,6 +52,17 @@ .checked-element { @extend .dropdown-element-base; + .icon { + @include flexCenter; + height: $s-24; + width: $s-24; + padding-right: $s-4; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + } + .label { flex-grow: 1; width: 100%; diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index 48d5bccf69..4147e44e31 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -7,7 +7,11 @@ (ns app.main.ui.icons (:refer-clojure :exclude [import mask]) (:require-macros [app.main.ui.icons :refer [icon-xref]]) - (:require [rumext.v2 :as mf])) + (:require + [app.common.data :as d] + [rumext.v2 :as mf])) + + ;; Keep the list of icons sorted @@ -320,6 +324,11 @@ (def detach-refactor (icon-xref :detach-refactor)) (def document-refactor (icon-xref :document-refactor)) (def drop-refactor (icon-xref :drop-refactor)) +(def easing-linear-refactor (icon-xref :easing-linear-refactor)) +(def easing-ease-refactor (icon-xref :easing-ease-refactor)) +(def easing-ease-in-refactor (icon-xref :easing-ease-in-refactor)) +(def easing-ease-out-refactor (icon-xref :easing-ease-out-refactor)) +(def easing-ease-in-out-refactor (icon-xref :easing-ease-in-out-refactor)) (def effects-refactor (icon-xref :effects-refactor)) (def elipse-refactor (icon-xref :elipse-refactor)) (def fill-content-refactor (icon-xref :fill-content-refactor)) @@ -348,6 +357,7 @@ (def hug-content-refactor (icon-xref :hug-content-refactor)) (def img-refactor (icon-xref :img-refactor)) (def icon-refactor (icon-xref :icon-refactor)) +(def interaction-refactor (icon-xref :interaction-refactor)) (def join-nodes-refactor (icon-xref :join-nodes-refactor)) (def justify-content-column-around-refactor (icon-xref :justify-content-column-around-refactor)) (def justify-content-column-between-refactor (icon-xref :justify-content-column-between-refactor)) @@ -466,3 +476,8 @@ [:div.icon-item {:key key} (deref val) [:span (pr-str key)]]))]) + +(defn key->icon + [icon-key] + (when icon-key + (get (ns-publics 'app.main.ui.icons) (symbol (d/name icon-key))))) 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 a7cc63c469..68781e1572 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 @@ -175,7 +175,7 @@ (and (= type :multiple) (some? (:hide-fill-on-export values)))) [:div {:class (stl/css :checkbox)} [:label {:for "show-fill-on-export" - :class (stl/css-case :checked (not hide-fill-on-export?))} + :class (stl/css-case :global/checked (not hide-fill-on-export?))} [:span {:class (stl/css-case :check-mark true :checked (not hide-fill-on-export?))} (when (not hide-fill-on-export?) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss index 477343ed7c..76786dc02b 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/fill.scss @@ -38,35 +38,10 @@ } } .checkbox { - display: flex; - align-items: center; + @extend .input-checkbox; margin-bottom: $s-8; margin-top: calc(-1 * $s-4); padding-left: $s-8; - input { - margin: 0; - } - label { - @include titleTipography; - display: flex; - align-items: center; - gap: $s-6; - cursor: pointer; - .check-mark { - @include flexCenter; - width: $s-16; - height: $s-16; - border-radius: $br-6; - background-color: var(--input-background-color); - &.checked { - background-color: var(--input-border-color-active); - svg { - @extend .button-icon-small; - stroke: var(--input-details-color); - } - } - } - } } } } diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs index 53ec895026..029a74f377 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs @@ -5,6 +5,7 @@ ;; Copyright (c) KALEIDOS INC (ns app.main.ui.workspace.sidebar.options.menus.interactions + (:require-macros [app.main.style :as stl]) (:require [app.common.data :as d] [app.common.data.macros :as dm] @@ -18,6 +19,10 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.numeric-input :refer [numeric-input*]] + [app.main.ui.components.radio-buttons :refer [radio-buttons radio-button]] + [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.icons :as i] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] @@ -92,12 +97,26 @@ :ease-out (tr "workspace.options.interaction-easing-ease-out") :ease-in-out (tr "workspace.options.interaction-easing-ease-in-out")}) +(defn- get-frames-options + [frames shape] + (->> frames + (filter #(and (not= (:id %) (:id shape)) ; A frame cannot navigate to itself + (not= (:id %) (:frame-id shape)))) ; nor a shape to its container frame + (map (fn [frame] + {:value (str (:id frame)) :label (:name frame)})))) + +(defn- get-shared-frames-options + [shared-frames] + (map (fn [frame] + {:value (str (:id frame)) :label (:name frame)}) shared-frames)) + (def flow-for-rename-ref (l/derived (l/in [:workspace-local :flow-for-rename]) st/state)) (mf/defc flow-item [{:keys [flow]}] - (let [editing? (mf/use-state false) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + editing? (mf/use-state false) flow-for-rename (mf/deref flow-for-rename-ref) name-ref (mf/use-ref) @@ -118,7 +137,22 @@ on-key-down (fn [event] (when (kbd/enter? event) (accept-edit)) - (when (kbd/esc? event) (cancel-edit)))] + (when (kbd/esc? event) (cancel-edit))) + + start-flow + (mf/use-fn + (mf/deps flow) + #(st/emit! (dw/select-shape (:starting-frame flow)))) + + rename-flow + (mf/use-fn + (mf/deps flow) + #(st/emit! (dwi/start-rename-flow (:id flow)))) + + remove-flow + (mf/use-fn + (mf/deps flow) + #(st/emit! (dwi/remove-flow (:id flow))))] (mf/use-effect (fn [] @@ -137,50 +171,97 @@ (let [name-input (mf/ref-val name-ref)] (dom/select-text! name-input)) nil)) + (if new-css-system + [:div {:class (stl/css :flow-element)} + [:button {:class (stl/css :start-flow-btn) + :on-click start-flow} + i/play-refactor] - [:div.flow-element - [:div.flow-button {:on-click #(st/emit! (dw/select-shape (:starting-frame flow)))} - i/play] - (if @editing? - [:input.element-name - {:type "text" - :ref name-ref - :on-blur accept-edit - :on-key-down on-key-down - :auto-focus true - :default-value (:name flow "")}] - [:span.element-label.flow-name - {:on-double-click #(st/emit! (dwi/start-rename-flow (:id flow)))} - (:name flow)]) - [:div.add-page {:on-click #(st/emit! (dwi/remove-flow (:id flow)))} - i/minus]])) + (if @editing? + [:input + {:class (stl/css :input-text) + :type "text" + :ref name-ref + :on-blur accept-edit + :on-key-down on-key-down + :auto-focus true + :default-value (:name flow "")}] + [:span + {:class (stl/css :flow-name-label) + :on-double-click rename-flow} + (:name flow)]) + [:div {:class (stl/css :remove-flow-btn) + :on-click remove-flow} + i/remove-refactor]] + + [:div.flow-element + [:div.flow-button {:on-click start-flow} i/play] + (if @editing? + [:input.element-name + {:type "text" + :ref name-ref + :on-blur accept-edit + :on-key-down on-key-down + :auto-focus true + :default-value (:name flow "")}] + [:span.element-label.flow-name + {:on-double-click rename-flow} + (:name flow)]) + [:div.add-page {:on-click remove-flow} i/minus]]))) (mf/defc page-flows [{:keys [flows]}] - (when (seq flows) - [:div.element-set.interactions-options - [:div.element-set-title - [:span (tr "workspace.options.flows.flow-starts")]] - (for [flow flows] - [:& flow-item {:flow flow :key (str (:id flow))}])])) + (let [new-css-system (mf/use-ctx ctx/new-css-system)] + (if new-css-system + (when (seq flows) + [:div {:class (stl/css :interaction-options)} + [:& title-bar {:collapsable? false + :title (tr "workspace.options.flows.flow-starts") + :class (stl/css :title-spacing-layout-flow)}] + (for [flow flows] + [:& flow-item {:flow flow :key (str (:id flow))}])]) + + (when (seq flows) + [:div.element-set.interactions-options + [:div.element-set-title + [:span (tr "workspace.options.flows.flow-starts")]] + (for [flow flows] + [:& flow-item {:flow flow :key (str (:id flow))}])])))) (mf/defc shape-flows [{:keys [flows shape]}] (when (= (:type shape) :frame) - (let [flow (ctp/get-frame-flow flows (:id shape))] - [:div.element-set.interactions-options - [:div.element-set-title - [:span (tr "workspace.options.flows.flow-start")]] - (if (nil? flow) - [:div.flow-element - [:span.element-label (tr "workspace.options.flows.add-flow-start")] - [:div.add-page {:on-click #(st/emit! (dwi/add-flow-selected-frame))} - i/plus]] - [:& flow-item {:flow flow :key (str (:id flow))}])]))) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + flow (ctp/get-frame-flow flows (:id shape)) + add-flow (mf/use-fn #(st/emit! (dwi/add-flow-selected-frame)))] + (if new-css-system + [:div {:class (stl/css :element-set)} + [:& title-bar {:collapsable? false + :title (tr "workspace.options.flows.flow") + :class (stl/css :title-spacing-layout-flow)} + (when (nil? flow) + [:button {:class (stl/css :add-flow-btn) + :title (tr "workspace.options.flows.add-flow-start") + :on-click add-flow} + i/add-refactor])] + + (when flow + [:& flow-item {:flow flow :key (str (:id flow))}])] + + [:div.element-set.interactions-options + [:div.element-set-title + [:span (tr "workspace.options.flows.flow-start")]] + (if (nil? flow) + [:div.flow-element + [:span.element-label (tr "workspace.options.flows.add-flow-start")] + [:div.add-page {:on-click add-flow} + i/plus]] + [:& flow-item {:flow flow :key (str (:id flow))}])])))) (mf/defc interaction-entry [{:keys [index shape interaction update-interaction remove-interaction]}] - (let [objects (deref refs/workspace-page-objects) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + objects (deref refs/workspace-page-objects) destination (get objects (:destination interaction)) frames (mf/with-memo [objects] (ctt/get-viewer-frames objects {:all-frames? true})) @@ -191,49 +272,75 @@ close-click-outside? (:close-click-outside interaction false) background-overlay? (:background-overlay interaction false) preserve-scroll? (:preserve-scroll interaction false) + way (-> interaction :animation :way) direction (-> interaction :animation :direction) - extended-open? (mf/use-state false) + state* (mf/use-state false) + extended-open? (deref state*) + + toggle-extended (mf/use-fn #(swap! state* not)) ext-delay-ref (mf/use-ref nil) ext-duration-ref (mf/use-ref nil) change-event-type - (fn [event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(ctsi/set-event-type % value shape)))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + (keyword event) + (-> event dom/get-target dom/get-value d/read-string))] + (update-interaction index #(ctsi/set-event-type % value shape))))) change-action-type - (fn [event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(ctsi/set-action-type % value)))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + (keyword event) + (-> event dom/get-target dom/get-value d/read-string))] + (update-interaction index #(ctsi/set-action-type % value))))) change-delay - (fn [value] - (update-interaction index #(ctsi/set-delay % value))) + (mf/use-fn + (mf/deps index) + (fn [value] + (update-interaction index #(ctsi/set-delay % value)))) change-destination - (fn [event] - (let [value (-> event dom/get-target dom/get-value) - value (when (not= value "") (uuid/uuid value))] - (update-interaction index #(ctsi/set-destination % value)))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + event + (-> event dom/get-target dom/get-value)) + value (when (not= value "") (uuid/uuid value))] + (update-interaction index #(ctsi/set-destination % value))))) change-position-relative-to - (fn [event] - (let [value (-> event - dom/get-target - dom/get-value - uuid/uuid)] - (update-interaction index #(ctsi/set-position-relative-to % value)))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + (uuid/uuid event) + (-> event + dom/get-target + dom/get-value + uuid/uuid))] + (update-interaction index #(ctsi/set-position-relative-to % value))))) change-preserve-scroll - (fn [event] + (mf/use-fn + (mf/deps index) + (fn [event] (let [value (-> event dom/get-target dom/checked?)] - (update-interaction index #(ctsi/set-preserve-scroll % value)))) + (update-interaction index #(ctsi/set-preserve-scroll % value))))) change-url - (fn [event] + (mf/use-fn + (mf/deps index) + (fn [event] (let [target (dom/get-target event) value (dom/get-value target) has-prefix? (or (str/starts-with? value "http://") @@ -247,321 +354,712 @@ (do (dom/remove-class! target "error") (update-interaction index #(ctsi/set-url % value))) - (dom/add-class! target "error")))) + (dom/add-class! target "error"))))) change-overlay-pos-type - (fn [shape-id event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(ctsi/set-overlay-pos-type % value shape objects)) - (when (= value :manual) - (update-interaction index #(ctsi/set-position-relative-to % shape-id))))) - + (mf/use-fn + (mf/deps shape) + (fn [event] + (let [shape-id (:id shape) + value (if new-css-system + event + (-> event dom/get-target dom/get-value d/read-string))] + (update-interaction index #(ctsi/set-overlay-pos-type % value shape objects)) + (when (= value :manual) + (update-interaction index #(ctsi/set-position-relative-to % shape-id)))))) toggle-overlay-pos-type - (fn [pos-type] - (update-interaction index #(ctsi/toggle-overlay-pos-type % pos-type shape objects))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [pos-type (-> (dom/get-current-target event) + (dom/get-data "value") + (keyword))] + (update-interaction index #(ctsi/toggle-overlay-pos-type % pos-type shape objects))))) change-close-click-outside - (fn [event] + (mf/use-fn + (mf/deps index) + (fn [event] (let [value (-> event dom/get-target dom/checked?)] - (update-interaction index #(ctsi/set-close-click-outside % value)))) + (update-interaction index #(ctsi/set-close-click-outside % value))))) change-background-overlay - (fn [event] + (mf/use-fn + (mf/deps index) + (fn [event] (let [value (-> event dom/get-target dom/checked?)] - (update-interaction index #(ctsi/set-background-overlay % value)))) + (update-interaction index #(ctsi/set-background-overlay % value))))) change-animation-type - (fn [event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(ctsi/set-animation-type % value)))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + (if (= "" event) + nil + (keyword event)) + (-> event dom/get-target dom/get-value d/read-string))] + (update-interaction index #(ctsi/set-animation-type % value))))) change-duration - (fn [value] - (update-interaction index #(ctsi/set-duration % value))) + (mf/use-fn(fn [value] + (update-interaction index #(ctsi/set-duration % value)))) change-easing - (fn [event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(ctsi/set-easing % value)))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + (keyword event) + (-> event dom/get-target dom/get-value d/read-string))] + (update-interaction index #(ctsi/set-easing % value))))) change-way - (fn [event] - (let [value (-> event dom/get-target dom/get-value d/read-string)] - (update-interaction index #(ctsi/set-way % value)))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + (keyword event) + (-> event dom/get-target dom/get-value d/read-string))] + (update-interaction index #(ctsi/set-way % value))))) change-direction - (fn [value] - (update-interaction index #(ctsi/set-direction % value))) + (mf/use-fn + (mf/deps index) + (fn [event] + (let [value (if new-css-system + (keyword event) + (-> event + dom/get-target + (dom/get-data "value") + keyword))] + (update-interaction index #(ctsi/set-direction % value))))) change-offset-effect - (fn [event] + (mf/use-fn + (mf/deps index) + (fn [event] (let [value (-> event dom/get-target dom/checked?)] - (update-interaction index #(ctsi/set-offset-effect % value))))] + (update-interaction index #(ctsi/set-offset-effect % value))))) - [:* - [:div.element-set-options-group {:class (dom/classnames - :open @extended-open?)} - ; Summary - [:div.element-set-actions-button {:on-click #(swap! extended-open? not)} - i/actions] - [:div.interactions-summary {:on-click #(swap! extended-open? not)} - [:div.trigger-name (event-type-name interaction)] - [:div.action-summary (action-summary interaction destination)]] - [:div.element-set-actions {:on-click #(remove-interaction index)} - [:div.element-set-actions-button i/minus]] + event-type-options [{:value :click :label (tr "workspace.options.interaction-on-click")} + ;; TODO: need more UX research + ;; :mouse-over (tr "workspace.options.interaction-while-hovering") + ;; :mouse-press (tr "workspace.options.interaction-while-pressing") + {:value :mouse-enter :label (tr "workspace.options.interaction-mouse-enter")} + {:value :mouse-leave :label (tr "workspace.options.interaction-mouse-leave")} + {:value :after-delay :label (tr "workspace.options.interaction-after-delay")}] - (when @extended-open? - [:div.element-set-content + action-type-options [{:value :navigate :label (tr "workspace.options.interaction-navigate-to")} + {:value :open-overlay :label (tr "workspace.options.interaction-open-overlay")} + {:value :toggle-overlay :label (tr "workspace.options.interaction-toggle-overlay")} + {:value :close-overlay :label (tr "workspace.options.interaction-close-overlay")} + {:value :prev-screen :label (tr "workspace.options.interaction-prev-screen")} + {:value :open-url :label (tr "workspace.options.interaction-open-url")}] - ;; Trigger select - [:div.interactions-element.separator - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:event-type interaction)) - :on-change change-event-type} - (for [[value name] (event-type-names)] - (when-not (and (= value :after-delay) - (not= (:type shape) :frame)) - [:option {:key (dm/str value) - :value (dm/str value)} name]))]] + frames-opts (get-frames-options frames shape) + + default-opts [(if (= (:action-type interaction) :close-overlay) + {:value "" :label (tr "workspace.options.interaction-self")} + {:value "" :label (tr "workspace.options.interaction-none")})] + destination-options + (mf/with-memo [frames-opts default-opts] + (d/concat-vec default-opts frames-opts)) + + shape-parents-opts (get-shared-frames-options shape-parents) + + relative-to-opts + (mf/with-memo [shape-parents-opts] + (if (not= (:overlay-pos-type interaction) :manual) + (d/concat-vec [{:value "" :label (tr "workspace.options.interaction-auto")}] + shape-parents-opts + [{:value (str (:id shape)) :label (str (:name shape) " (" (tr "workspace.options.interaction-self") ")")}]) + [{:value (str (:id shape)) :label (str (:name shape) " (" (tr "workspace.options.interaction-self") ")")}])) + + overlay-position-opts [{:value :manual :label (tr "workspace.options.interaction-pos-manual")} + {:value :center :label (tr "workspace.options.interaction-pos-center")} + {:value :top-left :label (tr "workspace.options.interaction-pos-top-left")} + {:value :top-right :label (tr "workspace.options.interaction-pos-top-right")} + {:value :top-center :label (tr "workspace.options.interaction-pos-top-center")} + {:value :bottom-left :label (tr "workspace.options.interaction-pos-bottom-left")} + {:value :bottom-right :label (tr "workspace.options.interaction-pos-bottom-right")} + {:value :bottom-center :label (tr "workspace.options.interaction-pos-bottom-center")}] + + basic-animation-opts [{:value "" :label (tr "workspace.options.interaction-animation-none")} + {:value :dissolve :label (tr "workspace.options.interaction-animation-dissolve")} + {:value :slide :label (tr "workspace.options.interaction-animation-slide")}] + + animation-opts + (mf/with-memo [basic-animation-opts] + (if (ctsi/allow-push? (:action-type interaction)) + (d/concat-vec basic-animation-opts [{:value :push :label (tr "workspace.options.interaction-animation-push")}]) + basic-animation-opts)) + + easing-options [{:icon :easing-linear-refactor :value :linear :label (tr "workspace.options.interaction-easing-linear")} + {:icon :easing-ease-refactor :value :ease :label (tr "workspace.options.interaction-easing-ease")} + {:icon :easing-ease-in-refactor :value :ease-in :label (tr "workspace.options.interaction-easing-ease-in")} + {:icon :easing-ease-out-refactor :value :ease-out :label (tr "workspace.options.interaction-easing-ease-out")} + {:icon :easing-ease-in-out-refactor :value :ease-in-out :label (tr "workspace.options.interaction-easing-ease-in-out")}]] + + + (if new-css-system + [:div {:class (stl/css-case :element-set-options-group true + :open extended-open?)} + ; Summary + [:div {:class (stl/css :interactions-summary)} + [:div {:class (stl/css :extend-btn) + :on-click toggle-extended} + i/menu-refactor] + + [:div {:class (stl/css :interactions-info) + :on-click toggle-extended} + [:div {:class (stl/css :trigger-name)} (event-type-name interaction)] + [:div {:class (stl/css :action-summary)} (action-summary interaction destination)]] + [:button {:class (stl/css :remove-btn) + :data-value index + :on-click #(remove-interaction index)} + i/remove-refactor]] + + (when extended-open? + [:div {:class (stl/css :extended-options)} + ;; Trigger select + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} + (tr "workspace.options.interaction-trigger")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (:event-type interaction) + :options event-type-options + :on-change change-event-type}]]] ;; Delay - (when (ctsi/has-delay interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-delay")] - [:div.input-element {:title (tr "workspace.options.interaction-ms")} - [:> numeric-input* {:ref ext-delay-ref - :on-change change-delay - :value (:delay interaction) - :title (tr "workspace.options.interaction-ms")}] - [:span.after (tr "workspace.options.interaction-ms")]]]) + (when (ctsi/has-delay interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} + (tr "workspace.options.interaction-delay")] + [:div {:class (stl/css :input-element-wrapper) + :title (tr "workspace.options.interaction-ms")} + [:span.after (tr "workspace.options.interaction-ms")] + [:> numeric-input* {:ref ext-delay-ref + :className (stl/css :numeric-input) + :on-change change-delay + :value (:delay interaction) + :title (tr "workspace.options.interaction-ms")}]]]) - ;; Action select - [:div.interactions-element.separator - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:action-type interaction)) - :on-change change-action-type} - (for [[value name] (action-type-names)] - [:option {:key (dm/str "action-" value) - :value (str value)} name])]] + ;; Action select + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-action")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (:action-type interaction) + :options action-type-options + :on-change change-action-type}]]] - ;; Destination - (when (ctsi/has-destination interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:destination interaction)) - :on-change change-destination} - (if (= (:action-type interaction) :close-overlay) - [:option {:value ""} (tr "workspace.options.interaction-self")] - [:option {:value ""} (tr "workspace.options.interaction-none")]) - (for [frame frames] - (when (and (not= (:id frame) (:id shape)) ; A frame cannot navigate to itself - (not= (:id frame) (:frame-id shape))) ; nor a shape to its container frame - [:option {:key (dm/str "destination-" (:id frame)) - :value (str (:id frame))} (:name frame)]))]]) + ;; Destination + (when (ctsi/has-destination interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-destination")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (str (:destination interaction)) + :options destination-options + :on-change change-destination}]]]) - ;; Preserve scroll - (when (ctsi/has-preserve-scroll interaction) - [:div.interactions-element - [:div.input-checkbox - [:input {:type "checkbox" - :id (str "preserve-" index) - :checked preserve-scroll? - :on-change change-preserve-scroll}] - [:label {:for (str "preserve-" index)} - (tr "workspace.options.interaction-preserve-scroll")]]]) + ;; Preserve scroll + (when (ctsi/has-preserve-scroll interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "preserve-" index) + :class (stl/css-case :global/checked preserve-scroll?)} + [:span {:class (stl/css-case :global/checked preserve-scroll?)} + (when preserve-scroll? + i/status-tick-refactor)] + (tr "workspace.options.interaction-preserve-scroll") + [:input {:type "checkbox" + :id (str "preserve-" index) + :checked preserve-scroll? + :on-change change-preserve-scroll}]]]]) - ;; URL - (when (ctsi/has-url interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-url")] - [:input.input-text {:type "url" - :placeholder "http://example.com" - :default-value (str (:url interaction)) - :on-blur change-url}]]) + ;; URL + (when (ctsi/has-url interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-url")] + [:div {:class (stl/css :input-element-wrapper)} + [:input {:class (stl/css :input-text) + :type "url" + :placeholder "http://example.com" + :default-value (str (:url interaction)) + :on-blur change-url}]]]) - (when (ctsi/has-overlay-opts interaction) - [:* - ;; Overlay position relative-to (select) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-relative-to")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:position-relative-to interaction)) - :on-change change-position-relative-to} - (when (not= (:overlay-pos-type interaction) :manual) - [:* - [:option {:value ""} (tr "workspace.options.interaction-auto")] - (for [frame shape-parents] - [:option {:key (dm/str "position-relative-to-" (:id frame)) - :value (str (:id frame))} (:name frame)])]) - [:option {:key (dm/str "position-relative-to-" (:id shape)) - :value (str (:id shape))} (:name shape) " (" (tr "workspace.options.interaction-self") ")"]]] + (when (ctsi/has-overlay-opts interaction) + [:* + ;; Overlay position relative-to (select) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-relative-to")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (str (:position-relative-to interaction)) + :options relative-to-opts + :on-change change-position-relative-to}]]] - ;; Overlay position (select) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-position")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (:overlay-pos-type interaction)) - :on-change (partial change-overlay-pos-type (:id shape))} - (for [[value name] (overlay-pos-type-names)] - [:option {:value (str value)} name])]] + ;; Overlay position (select) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-position")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :interaction-type-select) + :default-value (:overlay-pos-type interaction) + :options overlay-position-opts + :on-change change-overlay-pos-type}]]] - ;; Overlay position (buttons) - [:div.interactions-element.interactions-pos-buttons - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :center)) - :on-click #(toggle-overlay-pos-type :center)} - i/position-center] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :top-left)) - :on-click #(toggle-overlay-pos-type :top-left)} - i/position-top-left] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :top-right)) - :on-click #(toggle-overlay-pos-type :top-right)} - i/position-top-right] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :top-center)) - :on-click #(toggle-overlay-pos-type :top-center)} - i/position-top-center] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :bottom-left)) - :on-click #(toggle-overlay-pos-type :bottom-left)} - i/position-bottom-left] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :bottom-right)) - :on-click #(toggle-overlay-pos-type :bottom-right)} - i/position-bottom-right] - [:div.element-set-actions-button - {:class (dom/classnames :active (= overlay-pos-type :bottom-center)) - :on-click #(toggle-overlay-pos-type :bottom-center)} - i/position-bottom-center]] + ;; Overlay position (buttons) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :position-btns-wrapper)} + [:button {:class (stl/css-case :direction-btn true + :center-btn true + :active (= overlay-pos-type :center)) + :data-value :center + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :top-left-btn true + :active (= overlay-pos-type :top-left)) + :data-value :top-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :top-right-btn true + :active (= overlay-pos-type :top-right)) + :data-value :top-right + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] - ;; Overlay click outside - [:div.interactions-element - [:div.input-checkbox - [:input {:type "checkbox" - :id (str "close-" index) - :checked close-click-outside? - :on-change change-close-click-outside}] - [:label {:for (str "close-" index)} - (tr "workspace.options.interaction-close-outside")]]] + [:button {:class (stl/css-case :direction-btn true + :top-center-btn true + :active (= overlay-pos-type :top-center)) + :data-value :top-center + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-left-btn true + :active (= overlay-pos-type :bottom-left)) + :data-value :bottom-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-left-btn true + :active (= overlay-pos-type :bottom-left)) + :data-value :bottom-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + + [:button {:class (stl/css-case :direction-btn true + :bottom-left-btn true + :active (= overlay-pos-type :bottom-left)) + :data-value :bottom-left + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-right-btn true + :active (= overlay-pos-type :bottom-right)) + :data-value :bottom-right + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]] + [:button {:class (stl/css-case :direction-btn true + :bottom-center-btn true + :active (= overlay-pos-type :bottom-center)) + :data-value :bottom-center + :on-click toggle-overlay-pos-type} + [:span {:class (stl/css :rectangle)}]]]] + + ;; Overlay click outside + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "close-" index) + :class (stl/css-case :global/checked close-click-outside?)} + [:span {:class (stl/css-case :global/checked close-click-outside?)} + (when close-click-outside? + i/status-tick-refactor)] + (tr "workspace.options.interaction-close-outside") + [:input {:type "checkbox" + :id (str "close-" index) + :checked close-click-outside? + :on-change change-close-click-outside}]]]] ;; Overlay background + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "background-" index) + :class (stl/css-case :global/checked background-overlay?)} + [:span {:class (stl/css-case :global/checked background-overlay?)} + (when background-overlay? + i/status-tick-refactor)] + (tr "workspace.options.interaction-background") + [:input {:type "checkbox" + :id (str "background-" index) + :checked background-overlay? + :on-change change-background-overlay}]]]]]) + + (when (ctsi/has-animation? interaction) + [:* + ;; Animation select + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-animation")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :animation-select) + :default-value (or (-> interaction :animation :animation-type) "") + :options animation-opts + :on-change change-animation-type}]]] + + ;; Direction + (when (ctsi/has-way? interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :inputs-wrapper)} + + [:& radio-buttons {:selected (d/name way) + :on-change change-way + :name "animation-way"} + [:& radio-button {:value "in" + :id "animation-way-in"}] + [:& radio-button {:id "animation-way-out" + :value "out"}]]]]) + + ;; Direction + (when (ctsi/has-direction? interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :buttons-wrapper)} + [:& radio-buttons {:selected (d/name direction) + :on-change change-direction + :name "animation-direction"} + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :right) + :value "right" + :id "animation-right"}] + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :left) + :id "animation-left" + :value "left"}] + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :down) + :id "animation-down" + :value "down"}] + [:& radio-button {:icon i/column-refactor + :icon-class (stl/css :up) + :id "animation-up" + :value "up"}]]]]) + + ;; Duration + (when (ctsi/has-duration? interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-duration")] + [:div {:class (stl/css :input-element-wrapper) + :title (tr "workspace.options.interaction-ms")} + [:span.after (tr "workspace.options.interaction-ms")] + [:> numeric-input* {:ref ext-duration-ref + :on-change change-duration + :value (-> interaction :animation :duration) + :title (tr "workspace.options.interaction-ms")}]]]) + + ;; Easing + (when (ctsi/has-easing? interaction) + [:div {:class (stl/css :property-row)} + [:span {:class (stl/css :interaction-name)} (tr "workspace.options.interaction-easing")] + [:div {:class (stl/css :select-wrapper)} + [:& select {:class (stl/css :easing-select) + :default-value (-> interaction :animation :easing) + :options easing-options + :on-change change-easing}]]]) + + ;; Offset effect + (when (ctsi/has-offset-effect? interaction) + [:div {:class (stl/css :property-row)} + [:div {:class (stl/css :checkbox-option)} + [:label {:for (str "offset-effect-" index) + :class (stl/css-case :global/checked (-> interaction :animation :offset-effect))} + [:span {:class (stl/css-case :global/checked (-> interaction :animation :offset-effect))} + (when (-> interaction :animation :offset-effect) + i/status-tick-refactor)] + (tr "workspace.options.interaction-offset-effect") + [:input {:type "checkbox" + :id (str "offset-effect-" index) + :checked (-> interaction :animation :offset-effect) + :on-change change-offset-effect}]]]])])])] + + + [:div.element-set-options-group {:class (dom/classnames + :open extended-open?)} + ; Summary + [:div.element-set-actions-button {:on-click toggle-extended} + i/actions] + [:div.interactions-summary {:on-click toggle-extended} + [:div.trigger-name (event-type-name interaction)] + [:div.action-summary (action-summary interaction destination)]] + [:div.element-set-actions {:on-click #(remove-interaction index)} + [:div.element-set-actions-button i/minus]] + + (when extended-open? + [:div.element-set-content + + ;; Trigger select + [:div.interactions-element.separator + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")] + [:select.input-select + {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :value (str (:event-type interaction)) + :on-change change-event-type} + (for [[value name] (event-type-names)] + (when-not (and (= value :after-delay) + (not= (:type shape) :frame)) + [:option {:key (dm/str value) + :value (dm/str value)} name]))]] + + ;; Delay + (when (ctsi/has-delay interaction) + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-delay")] + [:div.input-element {:title (tr "workspace.options.interaction-ms")} + [:> numeric-input* {:ref ext-delay-ref + :on-change change-delay + :value (:delay interaction) + :title (tr "workspace.options.interaction-ms")}] + [:span.after (tr "workspace.options.interaction-ms")]]]) + + ;; Action select + [:div.interactions-element.separator + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")] + [:select.input-select + {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :value (str (:action-type interaction)) + :on-change change-action-type} + (for [[value name] (action-type-names)] + [:option {:key (dm/str "action-" value) + :value (str value)} name])]] + + ;; Destination + (when (ctsi/has-destination interaction) + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")] + [:select.input-select + {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :value (str (:destination interaction)) + :on-change change-destination} + (if (= (:action-type interaction) :close-overlay) + [:option {:value ""} (tr "workspace.options.interaction-self")] + [:option {:value ""} (tr "workspace.options.interaction-none")]) + (for [frame frames] + (when (and (not= (:id frame) (:id shape)) ; A frame cannot navigate to itself + (not= (:id frame) (:frame-id shape))) ; nor a shape to its container frame + [:option {:key (dm/str "destination-" (:id frame)) + :value (str (:id frame))} (:name frame)]))]]) + + ;; Preserve scroll + (when (ctsi/has-preserve-scroll interaction) [:div.interactions-element [:div.input-checkbox [:input {:type "checkbox" - :id (str "background-" index) - :checked background-overlay? - :on-change change-background-overlay}] - [:label {:for (str "background-" index)} - (tr "workspace.options.interaction-background")]]]]) + :id (str "preserve-" index) + :checked preserve-scroll? + :on-change change-preserve-scroll}] + [:label {:for (str "preserve-" index)} + (tr "workspace.options.interaction-preserve-scroll")]]]) - (when (ctsi/has-animation? interaction) - [:* - ;; Animation select - [:div.interactions-element.separator - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-animation")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (-> interaction :animation :animation-type)) - :on-change change-animation-type} - [:option {:value ""} (tr "workspace.options.interaction-animation-none")] - (for [[value name] (animation-type-names interaction)] - [:option {:value (str value)} name])]] + ;; URL + (when (ctsi/has-url interaction) + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-url")] + [:input.input-text {:type "url" + :placeholder "http://example.com" + :default-value (str (:url interaction)) + :on-blur change-url}]]) - ;; Direction - (when (ctsi/has-way? interaction) - [:div.interactions-element.interactions-way-buttons - [:div.input-radio - [:input {:type "radio" - :id "way-in" - :checked (= :in way) - :name "animation-way" - :value ":in" - :on-change change-way}] - [:label {:for "way-in"} (tr "workspace.options.interaction-in")]] - [:div.input-radio - [:input {:type "radio" - :id "way-out" - :checked (= :out way) - :name "animation-way" - :value ":out" - :on-change change-way}] - [:label {:for "way-out"} (tr "workspace.options.interaction-out")]]]) + (when (ctsi/has-overlay-opts interaction) + [:* + ;; Overlay position relative-to (select) + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-relative-to")] + [:select.input-select + {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :value (str (:position-relative-to interaction)) + :on-change change-position-relative-to} + (when (not= (:overlay-pos-type interaction) :manual) + [:* + [:option {:value ""} (tr "workspace.options.interaction-auto")] + (for [frame shape-parents] + [:option {:key (dm/str "position-relative-to-" (:id frame)) + :value (str (:id frame))} (:name frame)])]) + [:option {:key (dm/str "position-relative-to-" (:id shape)) + :value (str (:id shape))} (:name shape) " (" (tr "workspace.options.interaction-self") ")"]]] - ;; Direction - (when (ctsi/has-direction? interaction) - [:div.interactions-element.interactions-direction-buttons - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :right)) - :on-click #(change-direction :right)} - i/animate-right] - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :down)) - :on-click #(change-direction :down)} - i/animate-down] - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :left)) - :on-click #(change-direction :left)} - i/animate-left] - [:div.element-set-actions-button - {:class (dom/classnames :active (= direction :up)) - :on-click #(change-direction :up)} - i/animate-up]]) + ;; Overlay position (select) + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-position")] + [:select.input-select + {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :value (str (:overlay-pos-type interaction)) + :on-change change-overlay-pos-type} + (for [[value name] (overlay-pos-type-names)] + [:option {:value (str value)} name])]] - ;; Duration - (when (ctsi/has-duration? interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-duration")] - [:div.input-element {:title (tr "workspace.options.interaction-ms")} - [:> numeric-input* {:ref ext-duration-ref - :on-change change-duration - :value (-> interaction :animation :duration) - :title (tr "workspace.options.interaction-ms")}] - [:span.after (tr "workspace.options.interaction-ms")]]]) + ;; Overlay position (buttons) + [:div.interactions-element.interactions-pos-buttons + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :center)) + :data-value :center + :on-click toggle-overlay-pos-type} + i/position-center] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :top-left)) + :data-value :top-left + :on-click toggle-overlay-pos-type} + i/position-top-left] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :top-right)) + :data-value :top-right + :on-click toggle-overlay-pos-type} + i/position-top-right] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :top-center)) + :data-value :top-center + :on-click toggle-overlay-pos-type} + i/position-top-center] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :bottom-left)) + :data-value :bottom-center + :on-click toggle-overlay-pos-type} + i/position-bottom-left] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :bottom-right)) + :data-value :bottom-right + :on-click toggle-overlay-pos-type} + i/position-bottom-right] + [:div.element-set-actions-button + {:class (dom/classnames :active (= overlay-pos-type :bottom-center)) + :data-value :bottom-center + :on-click toggle-overlay-pos-type} + i/position-bottom-center]] - ;; Easing - (when (ctsi/has-easing? interaction) - [:div.interactions-element - [:span.element-set-subtitle.wide (tr "workspace.options.interaction-easing")] - [:select.input-select - {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element - :value (str (-> interaction :animation :easing)) - :on-change change-easing} - (for [[value name] (easing-names)] - [:option {:value (str value)} name])] - [:div.interactions-easing-icon - (case (-> interaction :animation :easing) - :linear i/easing-linear - :ease i/easing-ease - :ease-in i/easing-ease-in - :ease-out i/easing-ease-out - :ease-in-out i/easing-ease-in-out)]]) + ;; Overlay click outside + [:div.interactions-element + [:div.input-checkbox + [:input {:type "checkbox" + :id (str "close-" index) + :checked close-click-outside? + :on-change change-close-click-outside}] + [:label {:for (str "close-" index)} + (tr "workspace.options.interaction-close-outside")]]] - ;; Offset effect - (when (ctsi/has-offset-effect? interaction) - [:div.interactions-element - [:div.input-checkbox - [:input {:type "checkbox" - :id (str "offset-effect-" index) - :checked (-> interaction :animation :offset-effect) - :on-change change-offset-effect}] - [:label {:for (str "offset-effect-" index)} - (tr "workspace.options.interaction-offset-effect")]]])])])]])) + ;; Overlay background + [:div.interactions-element + [:div.input-checkbox + [:input {:type "checkbox" + :id (str "background-" index) + :checked background-overlay? + :on-change change-background-overlay}] + [:label {:for (str "background-" index)} + (tr "workspace.options.interaction-background")]]]]) + + (when (ctsi/has-animation? interaction) + [:* + ;; Animation select + [:div.interactions-element.separator + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-animation")] + [:select.input-select + {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :value (str (-> interaction :animation :animation-type)) + :on-change change-animation-type} + [:option {:value ""} (tr "workspace.options.interaction-animation-none")] + (for [[value name] (animation-type-names interaction)] + [:option {:value (str value)} name])]] + + ;; Direction + (when (ctsi/has-way? interaction) + [:div.interactions-element.interactions-way-buttons + [:div.input-radio + [:input {:type "radio" + :id "way-in" + :checked (= :in way) + :name "animation-way" + :value ":in" + :on-change change-way}] + [:label {:for "way-in"} (tr "workspace.options.interaction-in")]] + [:div.input-radio + [:input {:type "radio" + :id "way-out" + :checked (= :out way) + :name "animation-way" + :value ":out" + :on-change change-way}] + [:label {:for "way-out"} (tr "workspace.options.interaction-out")]]]) + + ;; Direction + (when (ctsi/has-direction? interaction) + [:div.interactions-element.interactions-direction-buttons + [:div.element-set-actions-button + {:class (dom/classnames :active (= direction :right)) + :data-value :right + :on-click change-direction} + i/animate-right] + [:div.element-set-actions-button + {:class (dom/classnames :active (= direction :down)) + :data-value :up + :on-click change-direction} + i/animate-down] + [:div.element-set-actions-button + {:class (dom/classnames :active (= direction :left)) + :data-value :left + :on-click change-direction} + i/animate-left] + [:div.element-set-actions-button + {:class (dom/classnames :active (= direction :up)) + :data-value :down + :on-click change-direction} + i/animate-up]]) + + ;; Duration + (when (ctsi/has-duration? interaction) + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-duration")] + [:div.input-element {:title (tr "workspace.options.interaction-ms")} + [:> numeric-input* {:ref ext-duration-ref + :on-change change-duration + :value (-> interaction :animation :duration) + :title (tr "workspace.options.interaction-ms")}] + [:span.after (tr "workspace.options.interaction-ms")]]]) + + ;; Easing + (when (ctsi/has-easing? interaction) + [:div.interactions-element + [:span.element-set-subtitle.wide (tr "workspace.options.interaction-easing")] + [:select.input-select + {:data-mousetrap-dont-stop true ;; makes mousetrap to not stop at this element + :value (str (-> interaction :animation :easing)) + :on-change change-easing} + (for [[value name] (easing-names)] + [:option {:value (str value)} name])] + [:div.interactions-easing-icon + (case (-> interaction :animation :easing) + :linear i/easing-linear + :ease i/easing-ease + :ease-in i/easing-ease-in + :ease-out i/easing-ease-out + :ease-in-out i/easing-ease-in-out)]]) + + ;; Offset effect + (when (ctsi/has-offset-effect? interaction) + [:div.interactions-element + [:div.input-checkbox + [:input {:type "checkbox" + :id (str "offset-effect-" index) + :checked (-> interaction :animation :offset-effect) + :on-change change-offset-effect}] + [:label {:for (str "offset-effect-" index)} + (tr "workspace.options.interaction-offset-effect")]]])])])]))) (mf/defc interactions-menu [{:keys [shape] :as props}] - (let [interactions (get shape :interactions []) + (let [new-css-system (mf/use-ctx ctx/new-css-system) + interactions (get shape :interactions []) options (mf/deref refs/workspace-page-options) flows (:flows options) @@ -577,35 +1075,76 @@ update-interaction (fn [index update-fn] (st/emit! (dwi/update-interaction shape index update-fn)))] - [:* - (if shape - [:& shape-flows {:flows flows - :shape shape}] - [:& page-flows {:flows flows}]) + (if new-css-system + [:div {:class (stl/css :interactions-content)} + (if shape + [:& shape-flows {:flows flows + :shape shape}] + [:& page-flows {:flows flows}]) + [:div {:class (stl/css :interaction-options)} + (when (and shape (not (cph/unframed-shape? shape))) + [:div {:class (stl/css :element-title)} + [:& title-bar {:collapsable? false + :title (tr "workspace.options.interactions") + :class (stl/css :title-spacing-layout-interactions)} - [:div.element-set.interactions-options - (when (and shape (not (cph/unframed-shape? shape))) - [:div.element-set-title - [:span (tr "workspace.options.interactions")] - [:div.add-page {:on-click add-interaction} - i/plus]]) - [:div.element-set-content - (when (= (count interactions) 0) - [:* - (when (and shape (not (cph/unframed-shape? shape))) - [:* - [:div.interactions-help-icon i/plus] - [:div.interactions-help.separator (tr "workspace.options.add-interaction")]]) - [:div.interactions-help-icon i/interaction] - [:div.interactions-help (tr "workspace.options.select-a-shape")] - [:div.interactions-help-icon i/play] - [:div.interactions-help (tr "workspace.options.use-play-button")]])] - [:div.groups - (for [[index interaction] (d/enumerate interactions)] - [:& interaction-entry {:key (dm/str (:id shape) "-" index) - :index index - :shape shape - :interaction interaction - :update-interaction update-interaction - :remove-interaction remove-interaction}])]]])) + [:button {:class (stl/css :add-interaction-btn) + :on-click add-interaction} + i/add-refactor]]]) + [:div {:class (stl/css :help-content)} + (when (= (count interactions) 0) + [:* + (when (and shape (not (cph/unframed-shape? shape))) + [:div {:class (stl/css :help-group)} + [:div {:class (stl/css :interactions-help-icon)} i/add-refactor] + [:div {:class (stl/css :interactions-help)} + (tr "workspace.options.add-interaction")]]) + [:div {:class (stl/css :help-group)} + [:div {:class (stl/css :interactions-help-icon)} i/interaction-refactor] + [:div {:class (stl/css :interactions-help)} + (tr "workspace.options.select-a-shape")]] + [:div {:class (stl/css :help-group)} + [:div {:class (stl/css :interactions-help-icon)} i/play-refactor] + [:div {:class (stl/css :interactions-help)} + (tr "workspace.options.use-play-button")]]])] + [:div {:class (stl/css :groups)} + (for [[index interaction] (d/enumerate interactions)] + [:& interaction-entry {:key (dm/str (:id shape) "-" index) + :index index + :shape shape + :interaction interaction + :update-interaction update-interaction + :remove-interaction remove-interaction}])]]] + + [:* + (if shape + [:& shape-flows {:flows flows + :shape shape}] + [:& page-flows {:flows flows}]) + + [:div.element-set.interactions-options + (when (and shape (not (cph/unframed-shape? shape))) + [:div.element-set-title + [:span (tr "workspace.options.interactions")] + [:div.add-page {:on-click add-interaction} + i/plus]]) + [:div.element-set-content + (when (= (count interactions) 0) + [:* + (when (and shape (not (cph/unframed-shape? shape))) + [:* + [:div.interactions-help-icon i/plus] + [:div.interactions-help.separator (tr "workspace.options.add-interaction")]]) + [:div.interactions-help-icon i/interaction] + [:div.interactions-help (tr "workspace.options.select-a-shape")] + [:div.interactions-help-icon i/play] + [:div.interactions-help (tr "workspace.options.use-play-button")]])] + [:div.groups + (for [[index interaction] (d/enumerate interactions)] + [:& interaction-entry {:key (dm/str (:id shape) "-" index) + :index index + :shape shape + :interaction interaction + :update-interaction update-interaction + :remove-interaction remove-interaction}])]]]))) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.scss new file mode 100644 index 0000000000..183d8a5f98 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.scss @@ -0,0 +1,244 @@ +// 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 + +@import "refactor/common-refactor.scss"; + +.interactions-content { + display: flex; + flex-direction: column; + gap: $s-8; + .interaction-options { + display: flex; + flex-direction: column; + gap: $s-4; + .element-title { + .add-interaction-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + } + } + } + + .help-content { + .help-group { + margin-bottom: $s-40; + .interactions-help-icon { + @include flexCenter; + width: $s-48; + height: $s-48; + border-radius: $br-circle; + background-color: var(--pill-background-color); + margin: 0 auto $s-12 auto; + svg { + @extend .button-icon; + stroke: var(--icon-foreground); + height: $s-32; + width: $s-32; + } + } + .interactions-help { + @include titleTipography; + text-align: center; + } + } + } + .groups { + display: flex; + flex-direction: column; + gap: $s-4; + .element-set-options-group { + &.open { + display: flex; + flex-direction: column; + gap: $s-4; + .extended-options { + display: flex; + flex-direction: column; + gap: $s-4; + .property-row { + @extend .attr-row; + .interaction-name { + @include twoLineTextEllipsis; + @include titleTipography; + padding-left: $s-4; + width: $s-92; + margin: auto 0; + grid-area: name; + } + .select-wrapper { + display: flex; + align-items: center; + grid-area: content; + } + .input-element-wrapper { + @extend .input-element; + grid-area: content; + } + .checkbox-option { + @extend .input-checkbox; + grid-area: content; + } + .position-btns-wrapper { + grid-area: content; + display: grid; + grid-template-areas: + "topleft top topright" + "left center right" + "bottomleft bottom bottomright"; + grid-template-columns: repeat(3, 1fr); + grid-template-rows: repeat(3, 1fr); + width: $s-84; + height: $s-84; + border-radius: $br-8; + background-color: var(--color-background-tertiary); + .direction-btn { + @extend .button-tertiary; + height: $s-28; + width: $s-28; + .rectangle { + height: $s-8; + width: $s-8; + background-color: var(--color-background-quaternary); + } + &:hover { + .rectangle { + background-color: var(--color-accent-primary); + } + } + &.active { + background-color: var(--color-background-quaternary); + .rectangle { + background-color: var(--color-accent-primary); + } + } + } + .center-btn { + grid-area: center; + } + .top-left-btn { + grid-area: topleft; + } + .top-right-btn { + grid-area: topright; + } + .top-center-btn { + grid-area: top; + } + .bottom-left-btn { + grid-area: bottomleft; + } + .bottom-right-btn { + grid-area: bottomright; + } + .bottom-center-btn { + grid-area: bottom; + } + } + .buttons-wrapper { + grid-area: content; + .right svg { + transform: rotate(-90deg); + } + .left svg { + transform: rotate(90deg); + } + .up svg { + transform: rotate(180deg); + } + } + .inputs-wrapper { + grid-area: content; + display: flex; + align-items: center; + gap: $s-4; + .radio-btn { + @extend .input-checkbox; + } + } + } + } + } + + .interactions-summary { + @extend .asset-element; + height: $s-44; + padding: 0; + gap: $s-4; + .extend-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } + } + + .interactions-info { + flex-grow: 1; + .trigger-name { + color: white; + } + .action-summary { + color: var(--color-foreground-secondary); + } + } + .remove-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } + } + } + } + } + } + .element-set { + display: flex; + flex-direction: column; + gap: $s-4; + .add-flow-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } + } + } +} +.flow-element { + @extend .asset-element; + padding: 0; + gap: $s-4; + .start-flow-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } + } + .input-text { + @extend .input-base; + } + .flow-name-label { + flex-grow: 1; + } + .remove-flow-btn { + @extend .button-tertiary; + height: $s-32; + width: $s-28; + svg { + @extend .button-icon; + } + } +} diff --git a/frontend/translations/en.po b/frontend/translations/en.po index daed823526..56ec34a5af 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -3676,6 +3676,10 @@ msgstr "Fill" msgid "workspace.options.flows.add-flow-start" msgstr "Add flow start" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.flows.flow" +msgstr "Flow" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.flows.flow-start" msgstr "Flow start" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 45b565ce23..3850af90fc 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -3763,6 +3763,10 @@ msgstr "AƱadir inicio de flujo" msgid "workspace.options.flows.flow-start" msgstr "Inicio de flujo" +#: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +msgid "workspace.options.flows.flow" +msgstr "Flujo" + #: src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs msgid "workspace.options.flows.flow-starts" msgstr "Inicios de flujo"