Merge pull request #3680 from penpot/eva-redesign-prototype-tab

💄 Update prototype tab desgin with new UI
This commit is contained in:
Aitor Moreno 2023-10-05 16:47:31 +02:00 committed by GitHub
commit 77964604fd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1252 additions and 385 deletions

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2C4.667 2 11.333 14 2 14"/>
</svg>

After

Width:  |  Height:  |  Size: 159 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M2 14c8.667 0 12-12 12-12"/>
</svg>

After

Width:  |  Height:  |  Size: 156 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2C5.333 2 2 14 2 14"/>
</svg>

After

Width:  |  Height:  |  Size: 154 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2C2 2 7.333 14 2 14"/>
</svg>

After

Width:  |  Height:  |  Size: 154 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M2.667 13.333L13.333 2.667"/>
</svg>

After

Width:  |  Height:  |  Size: 157 B

View file

@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" stroke-linecap="round" stroke-linejoin="round">
<path d="M5 7.833h6m0 0l-2.469-2.5M11 7.833l-2.469 2.5M8 15A7 7 0 108 1a7 7 0 000 14z"/>
</svg>

After

Width:  |  Height:  |  Size: 207 B

View file

@ -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;

View file

@ -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);

View file

@ -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]

View file

@ -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%;

View file

@ -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)))))

View file

@ -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?)

View file

@ -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);
}
}
}
}
}
}
}

View file

@ -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,10 +171,31 @@
(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]
(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 #(st/emit! (dw/select-shape (:starting-frame flow)))}
i/play]
[:div.flow-button {:on-click start-flow} i/play]
(if @editing?
[:input.element-name
{:type "text"
@ -150,37 +205,63 @@
:auto-focus true
:default-value (:name flow "")}]
[:span.element-label.flow-name
{:on-double-click #(st/emit! (dwi/start-rename-flow (:id flow)))}
{:on-double-click rename-flow}
(:name flow)])
[:div.add-page {:on-click #(st/emit! (dwi/remove-flow (:id flow)))}
i/minus]]))
[:div.add-page {:on-click remove-flow} i/minus]])))
(mf/defc page-flows
[{:keys [flows]}]
(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))}])]))
[:& 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))]
(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 #(st/emit! (dwi/add-flow-selected-frame))}
[:div.add-page {:on-click add-flow}
i/plus]]
[:& flow-item {:flow flow :key (str (:id flow))}])])))
[:& 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,48 +272,74 @@
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
(mf/use-fn
(mf/deps index)
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(ctsi/set-event-type % value shape))))
(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
(mf/use-fn
(mf/deps index)
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(ctsi/set-action-type % value))))
(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
(mf/use-fn
(mf/deps index)
(fn [value]
(update-interaction index #(ctsi/set-delay % value)))
(update-interaction index #(ctsi/set-delay % value))))
change-destination
(mf/use-fn
(mf/deps index)
(fn [event]
(let [value (-> event dom/get-target dom/get-value)
(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))))
(update-interaction index #(ctsi/set-destination % value)))))
change-position-relative-to
(mf/use-fn
(mf/deps index)
(fn [event]
(let [value (-> 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))))
uuid/uuid))]
(update-interaction index #(ctsi/set-position-relative-to % value)))))
change-preserve-scroll
(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
(mf/use-fn
(mf/deps index)
(fn [event]
(let [target (dom/get-target event)
value (dom/get-value target)
@ -247,72 +354,451 @@
(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)]
(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)))))
(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
(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
(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
(mf/use-fn
(mf/deps index)
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(ctsi/set-animation-type % value))))
(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
(mf/use-fn
(mf/deps index)
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(ctsi/set-easing % value))))
(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
(mf/use-fn
(mf/deps index)
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(ctsi/set-way % value))))
(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
(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?)}
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")}]
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")}]
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.element-set-actions-button {:on-click #(swap! extended-open? not)}
[: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 {: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 {: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 {: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 {: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 {: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 {: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 {: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 {: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)}]]
[: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 #(swap! extended-open? not)}
[: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?
(when extended-open?
[:div.element-set-content
;; Trigger select
@ -411,7 +897,7 @@
[: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))}
:on-change change-overlay-pos-type}
(for [[value name] (overlay-pos-type-names)]
[:option {:value (str value)} name])]]
@ -419,31 +905,38 @@
[: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)}
: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))
:on-click #(toggle-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))
:on-click #(toggle-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))
:on-click #(toggle-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))
:on-click #(toggle-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))
:on-click #(toggle-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))
:on-click #(toggle-overlay-pos-type :bottom-center)}
:data-value :bottom-center
:on-click toggle-overlay-pos-type}
i/position-bottom-center]]
;; Overlay click outside
@ -504,19 +997,23 @@
[:div.interactions-element.interactions-direction-buttons
[:div.element-set-actions-button
{:class (dom/classnames :active (= direction :right))
:on-click #(change-direction :right)}
:data-value :right
:on-click change-direction}
i/animate-right]
[:div.element-set-actions-button
{:class (dom/classnames :active (= direction :down))
:on-click #(change-direction :down)}
:data-value :up
:on-click change-direction}
i/animate-down]
[:div.element-set-actions-button
{:class (dom/classnames :active (= direction :left))
:on-click #(change-direction :left)}
:data-value :left
:on-click change-direction}
i/animate-left]
[:div.element-set-actions-button
{:class (dom/classnames :active (= direction :up))
:on-click #(change-direction :up)}
:data-value :down
:on-click change-direction}
i/animate-up]])
;; Duration
@ -557,11 +1054,12 @@
:checked (-> interaction :animation :offset-effect)
:on-change change-offset-effect}]
[:label {:for (str "offset-effect-" index)}
(tr "workspace.options.interaction-offset-effect")]]])])])]]))
(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,6 +1075,47 @@
update-interaction
(fn [index update-fn]
(st/emit! (dwi/update-interaction shape index update-fn)))]
(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)}
[: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
@ -607,5 +1146,5 @@
:shape shape
:interaction interaction
:update-interaction update-interaction
:remove-interaction remove-interaction}])]]]))
:remove-interaction remove-interaction}])]]])))

View file

@ -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;
}
}
}

View file

@ -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"

View file

@ -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"