mirror of
https://github.com/penpot/penpot.git
synced 2025-01-20 05:34:23 -05:00
Merge pull request #353 from tokens-studio/eva-fix-context-menu
♻️ Fix context menu
This commit is contained in:
commit
a9e20391d9
8 changed files with 199 additions and 119 deletions
|
@ -86,6 +86,11 @@
|
|||
(mf/with-effect [default-value]
|
||||
(swap! state* assoc :current-value default-value))
|
||||
|
||||
(mf/with-effect [is-open?]
|
||||
(when (and (not= 0 (mf/ref-val dropdown-direction-change*)) (= false is-open?))
|
||||
(reset! dropdown-direction* "down")
|
||||
(mf/set-ref-val! dropdown-direction-change* 0)))
|
||||
|
||||
(mf/with-effect [is-open? dropdown-element*]
|
||||
(let [dropdown-element (mf/ref-val dropdown-element*)]
|
||||
(when (and (= 0 (mf/ref-val dropdown-direction-change*)) dropdown-element)
|
||||
|
|
|
@ -129,7 +129,7 @@
|
|||
|
||||
(mf/defc asset-section
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [children file-id title section assets-count icon open?]}]
|
||||
[{:keys [children file-id title section assets-count icon open? on-click]}]
|
||||
(let [children (-> (array/normalize-to-array children)
|
||||
(array/without-nils))
|
||||
|
||||
|
@ -159,7 +159,8 @@
|
|||
|
||||
[:div {:class (stl/css-case :asset-section true
|
||||
:opened (and (< 0 assets-count)
|
||||
open?))}
|
||||
open?))
|
||||
:on-click on-click}
|
||||
[:& title-bar
|
||||
{:collapsable (< 0 assets-count)
|
||||
:collapsed (not open?)
|
||||
|
|
|
@ -8,17 +8,19 @@
|
|||
(:require-macros [app.main.style :as stl])
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.types.tokens-lib :as ctob]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.data.tokens :as dt]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.components.dropdown :refer [dropdown]]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||
[app.main.ui.workspace.tokens.changes :as wtch]
|
||||
[app.main.ui.workspace.tokens.token :as wtt]
|
||||
[app.main.ui.workspace.tokens.token-types :as wtty]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.i18n :refer [tr]]
|
||||
[app.util.timers :as timers]
|
||||
[okulary.core :as l]
|
||||
[rumext.v2 :as mf]))
|
||||
|
@ -58,7 +60,7 @@
|
|||
all-action (let [props {:attributes attributes
|
||||
:token token
|
||||
:shape-ids shape-ids}]
|
||||
{:title "All"
|
||||
{:title (tr "labels.all")
|
||||
:selected? all-selected?
|
||||
:action #(if all-selected?
|
||||
(st/emit! (wtch/unapply-token props))
|
||||
|
@ -96,7 +98,7 @@
|
|||
vertical-padding-selected? (and
|
||||
(not all-selected?)
|
||||
(every? selected-pred vertical-attributes))
|
||||
padding-items [{:title "All"
|
||||
padding-items [{:title (tr "labels.all")
|
||||
:selected? all-selected?
|
||||
:action (fn []
|
||||
(let [props {:attributes all-padding-attrs
|
||||
|
@ -199,6 +201,7 @@
|
|||
{:title "Sizing" :submenu :sizing}
|
||||
:separator
|
||||
{:title "Border Radius" :submenu :border-radius}]
|
||||
[:separator]
|
||||
(stroke-width context-data)
|
||||
[:separator]
|
||||
(generic-attribute-actions #{:x} "X" (assoc context-data :on-update-shape wtch/update-shape-position))
|
||||
|
@ -206,11 +209,8 @@
|
|||
|
||||
(defn default-actions [{:keys [token selected-token-set-path]}]
|
||||
(let [{:keys [modal]} (wtty/get-token-properties token)]
|
||||
[{:title "Delete Token"
|
||||
:action #(st/emit! (dt/delete-token (ctob/set-path->set-name selected-token-set-path) (:name token)))}
|
||||
{:title "Duplicate Token"
|
||||
:action #(st/emit! (dt/duplicate-token (:name token)))}
|
||||
{:title "Edit Token"
|
||||
[{:title (tr "workspace.token.edit")
|
||||
:no-selectable true
|
||||
:action (fn [event]
|
||||
(let [{:keys [key fields]} modal]
|
||||
(st/emit! dt/hide-token-context-menu)
|
||||
|
@ -222,9 +222,11 @@
|
|||
:action "edit"
|
||||
:selected-token-set-path selected-token-set-path
|
||||
:token token})))}
|
||||
{:title "Duplicate Token"
|
||||
{:title (tr "workspace.token.duplicate")
|
||||
:no-selectable true
|
||||
:action #(st/emit! (dt/duplicate-token (:name token)))}
|
||||
{:title "Delete Token"
|
||||
{:title (tr "workspace.token.delete")
|
||||
:no-selectable true
|
||||
:action #(st/emit! (-> selected-token-set-path
|
||||
ctob/prefixed-set-path-string->set-name-string
|
||||
(dt/delete-token (:name token))))}]))
|
||||
|
@ -237,6 +239,12 @@
|
|||
(when (seq attribute-actions) [:separator])
|
||||
(default-actions context-data))))
|
||||
|
||||
(defn submenu-actions-selection-actions [{:keys [type token] :as context-data}]
|
||||
(let [with-actions (get shape-attribute-actions-map (or type (:type token)))
|
||||
attribute-actions (if with-actions (with-actions context-data) [])]
|
||||
(concat
|
||||
attribute-actions)))
|
||||
|
||||
;; Components ------------------------------------------------------------------
|
||||
|
||||
(def tokens-menu-ref
|
||||
|
@ -249,69 +257,93 @@
|
|||
|
||||
(mf/defc menu-entry
|
||||
{::mf/props :obj}
|
||||
[{:keys [title value on-click selected? children submenu-offset]}]
|
||||
[{:keys [title value on-click selected? children submenu-offset submenu-direction no-selectable]}]
|
||||
(let [submenu-ref (mf/use-ref nil)
|
||||
hovering? (mf/use-ref false)
|
||||
on-pointer-enter
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(mf/set-ref-val! hovering? true)
|
||||
(when-let [submenu-node (mf/ref-val submenu-ref)]
|
||||
(dom/set-css-property! submenu-node "display" "block"))))
|
||||
|
||||
on-pointer-leave
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn []
|
||||
(mf/set-ref-val! hovering? false)
|
||||
(when-let [submenu-node (mf/ref-val submenu-ref)]
|
||||
(timers/schedule 50 #(when-not (mf/ref-val hovering?)
|
||||
(dom/set-css-property! submenu-node "display" "none"))))))
|
||||
|
||||
set-dom-node
|
||||
(mf/use-callback
|
||||
(mf/use-fn
|
||||
(fn [dom]
|
||||
(let [submenu-node (mf/ref-val submenu-ref)]
|
||||
(when (and (some? dom) (some? submenu-node))
|
||||
(dom/set-css-property! submenu-node "top" (str (.-offsetTop dom) "px"))))))]
|
||||
[:li
|
||||
{:class (stl/css :context-menu-item)
|
||||
:ref set-dom-node
|
||||
:data-value value
|
||||
:on-click on-click
|
||||
:on-pointer-enter on-pointer-enter
|
||||
:on-pointer-leave on-pointer-leave}
|
||||
(when (and (some? dom) (some? submenu-node) (= submenu-direction "up"))
|
||||
(dom/set-css-property! submenu-node "top" "unset"))
|
||||
(when (and (some? dom) (some? submenu-node) (= submenu-direction "down"))
|
||||
(dom/set-css-property! submenu-node "top" (dm/str (.-offsetTop dom) "px"))))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps submenu-direction)
|
||||
(fn []
|
||||
(let [submenu-node (mf/ref-val submenu-ref)]
|
||||
(when (= submenu-direction "up")
|
||||
(dom/set-css-property! submenu-node "top" "unset")))))
|
||||
|
||||
[:li {:class (stl/css :context-menu-item)
|
||||
:ref set-dom-node
|
||||
:data-value value
|
||||
:on-click on-click
|
||||
:on-pointer-enter on-pointer-enter
|
||||
:on-pointer-leave on-pointer-leave}
|
||||
(when selected?
|
||||
[:span {:class (stl/css :icon-wrapper)}
|
||||
[:span {:class (stl/css :selected-icon)} i/tick]])
|
||||
[:span {:class (stl/css :title)} title]
|
||||
[:> icon* {:id "tick" :size "s" :class (stl/css :icon-wrapper)}])
|
||||
[:span {:class (stl/css-case :item-text true
|
||||
:item-with-icon-space (and
|
||||
(not selected?)
|
||||
(not no-selectable)))}
|
||||
title]
|
||||
(when children
|
||||
[:*
|
||||
[:span {:class (stl/css :submenu-icon)} i/arrow]
|
||||
[:> icon* {:id "arrow" :size "s"}]
|
||||
[:ul {:class (stl/css :token-context-submenu)
|
||||
:data-direction submenu-direction
|
||||
:ref submenu-ref
|
||||
;; Under review: This distances are arbitrary,
|
||||
;; https://tree.taiga.io/project/penpot/task/9627
|
||||
:style {:display "none"
|
||||
:top 0
|
||||
:left (str submenu-offset "px")}
|
||||
:--dist (if (= submenu-direction "down")
|
||||
"-80px"
|
||||
"80px")
|
||||
:left (dm/str submenu-offset "px")}
|
||||
:on-context-menu prevent-default}
|
||||
children]])]))
|
||||
|
||||
(mf/defc menu-tree
|
||||
[{:keys [selected-shapes] :as context-data}]
|
||||
[{:keys [selected-shapes submenu-offset submenu-direction type] :as context-data}]
|
||||
(let [entries (if (seq selected-shapes)
|
||||
(selection-actions context-data)
|
||||
(if (some? type)
|
||||
(submenu-actions-selection-actions context-data)
|
||||
(selection-actions context-data))
|
||||
(default-actions context-data))]
|
||||
(for [[index {:keys [title action selected? submenu] :as entry}] (d/enumerate entries)]
|
||||
[:* {:key (str title " " index)}
|
||||
(for [[index {:keys [title action selected? submenu no-selectable] :as entry}] (d/enumerate entries)]
|
||||
[:* {:key (dm/str title " " index)}
|
||||
(cond
|
||||
(= :separator entry) [:li {:class (stl/css :separator)}]
|
||||
submenu [:& menu-entry {:title title
|
||||
:submenu-offset (:submenu-offset context-data)}
|
||||
:no-selectable true
|
||||
:submenu-direction submenu-direction
|
||||
:submenu-offset submenu-offset}
|
||||
[:& menu-tree (assoc context-data :type submenu)]]
|
||||
:else [:& menu-entry
|
||||
{:title title
|
||||
:on-click action
|
||||
:no-selectable no-selectable
|
||||
:selected? selected?}])])))
|
||||
|
||||
(mf/defc token-context-menu-tree
|
||||
[{:keys [width] :as mdata}]
|
||||
[{:keys [width direction] :as mdata}]
|
||||
(let [objects (mf/deref refs/workspace-page-objects)
|
||||
selected (mf/deref refs/selected-shapes)
|
||||
selected-shapes (into [] (keep (d/getf objects)) selected)
|
||||
|
@ -320,27 +352,51 @@
|
|||
selected-token-set-path (mf/deref refs/workspace-selected-token-set-path)]
|
||||
[:ul {:class (stl/css :context-list)}
|
||||
[:& menu-tree {:submenu-offset width
|
||||
:submenu-direction direction
|
||||
:token token
|
||||
:selected-token-set-path selected-token-set-path
|
||||
:selected-shapes selected-shapes}]]))
|
||||
|
||||
(mf/defc token-context-menu
|
||||
[]
|
||||
(let [mdata (mf/deref tokens-menu-ref)
|
||||
top (+ (get-in mdata [:position :y]) 5)
|
||||
left (+ (get-in mdata [:position :x]) 5)
|
||||
width (mf/use-state 0)
|
||||
dropdown-ref (mf/use-ref)]
|
||||
(let [mdata (mf/deref tokens-menu-ref)
|
||||
is-open? (boolean mdata)
|
||||
width (mf/use-state 0)
|
||||
dropdown-ref (mf/use-ref)
|
||||
dropdown-direction* (mf/use-state "down")
|
||||
dropdown-direction (deref dropdown-direction*)
|
||||
dropdown-direction-change* (mf/use-ref 0)
|
||||
top (+ (get-in mdata [:position :y]) 5)
|
||||
left (+ (get-in mdata [:position :x]) 5)]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps mdata)
|
||||
(mf/deps is-open?)
|
||||
(fn []
|
||||
(when-let [node (mf/ref-val dropdown-ref)]
|
||||
(reset! width (.-offsetWidth node)))))
|
||||
[:& dropdown {:show (boolean mdata)
|
||||
|
||||
(mf/with-effect [is-open?]
|
||||
(when (and (not= 0 (mf/ref-val dropdown-direction-change*)) (= false is-open?))
|
||||
(reset! dropdown-direction* "down")
|
||||
(mf/set-ref-val! dropdown-direction-change* 0)))
|
||||
|
||||
(mf/with-effect [is-open? dropdown-ref]
|
||||
(let [dropdown-element (mf/ref-val dropdown-ref)]
|
||||
(when (and (= 0 (mf/ref-val dropdown-direction-change*)) dropdown-element)
|
||||
(let [is-outside? (dom/is-element-outside? dropdown-element)]
|
||||
(reset! dropdown-direction* (if is-outside? "up" "down"))
|
||||
(mf/set-ref-val! dropdown-direction-change* (inc (mf/ref-val dropdown-direction-change*)))))))
|
||||
|
||||
[:& dropdown {:show is-open?
|
||||
:on-close #(st/emit! dt/hide-token-context-menu)}
|
||||
[:div {:class (stl/css :token-context-menu)
|
||||
:ref dropdown-ref
|
||||
:style {:top top :left left}
|
||||
:data-direction dropdown-direction
|
||||
:style {:--bottom (if (= dropdown-direction "up")
|
||||
"40px"
|
||||
"unset")
|
||||
:--top (dm/str top "px")
|
||||
:left (dm/str left "px")}
|
||||
:on-context-menu prevent-default}
|
||||
(when mdata
|
||||
[:& token-context-menu-tree (assoc mdata :offset @width)])]]))
|
||||
[:& token-context-menu-tree (assoc mdata :width @width :direction dropdown-direction)])]]))
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Copyright (c) KALEIDOS INC
|
||||
|
||||
@use "../../ds/typography.scss" as *;
|
||||
@import "refactor/common-refactor.scss";
|
||||
|
||||
.token-context-menu {
|
||||
|
@ -11,6 +12,14 @@
|
|||
z-index: $z-index-4;
|
||||
}
|
||||
|
||||
.token-context-menu[data-direction="up"] {
|
||||
bottom: var(--bottom);
|
||||
}
|
||||
|
||||
.token-context-menu[data-direction="down"] {
|
||||
top: var(--top);
|
||||
}
|
||||
|
||||
.context-list,
|
||||
.token-context-submenu {
|
||||
@include menuShadow;
|
||||
|
@ -18,15 +27,18 @@
|
|||
width: $s-240;
|
||||
padding: $s-4;
|
||||
border-radius: $br-8;
|
||||
border: $s-2 solid var(--panel-border-color);
|
||||
background-color: var(--menu-background-color);
|
||||
border: $s-2 solid var(--color-background-quaternary);
|
||||
background-color: var(--color-background-tertiary);
|
||||
max-height: 100vh;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
li {
|
||||
@include bodySmallTypography;
|
||||
color: var(--menu-foreground-color);
|
||||
}
|
||||
.token-context-submenu[data-direction="up"] {
|
||||
bottom: var(--dist);
|
||||
}
|
||||
|
||||
.token-context-submenu[data-direction="down"] {
|
||||
top: var(--dist);
|
||||
}
|
||||
|
||||
.token-context-submenu {
|
||||
|
@ -36,68 +48,46 @@
|
|||
}
|
||||
|
||||
.separator {
|
||||
@include bodySmallTypography;
|
||||
margin: $s-6;
|
||||
border-block-start: $s-1 solid var(--panel-border-color);
|
||||
}
|
||||
|
||||
.context-menu-item {
|
||||
--context-menu-item-bg-color: none;
|
||||
--context-menu-item-fg-color: var(--color-foreground-primary);
|
||||
--context-menu-item-border-color: none;
|
||||
@include use-typography("body-small");
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: $s-28;
|
||||
width: 100%;
|
||||
padding: $s-6;
|
||||
padding: $s-8;
|
||||
border-radius: $br-8;
|
||||
color: var(--context-menu-item-fg-color);
|
||||
background-color: var(--context-menu-item-bg-color);
|
||||
border: $s-1 solid var(--context-menu-item-border-color);
|
||||
cursor: pointer;
|
||||
|
||||
.title {
|
||||
flex-grow: 1;
|
||||
@include bodySmallTypography;
|
||||
color: var(--menu-foreground-color);
|
||||
margin-left: calc(($s-32 + $s-28) / 2);
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
.icon-wrapper + .title {
|
||||
margin-left: $s-6;
|
||||
}
|
||||
|
||||
.selected-icon {
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: var(--menu-foreground-color);
|
||||
}
|
||||
}
|
||||
|
||||
.submenu-icon {
|
||||
margin-left: $s-2;
|
||||
svg {
|
||||
@extend .button-icon-small;
|
||||
stroke: var(--menu-foreground-color);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--menu-background-color-hover);
|
||||
.title {
|
||||
color: var(--menu-foreground-color-hover);
|
||||
}
|
||||
.shortcut {
|
||||
color: var(--menu-shortcut-foreground-color-hover);
|
||||
}
|
||||
--context-menu-item-bg-color: var(--color-background-quaternary);
|
||||
}
|
||||
|
||||
&:focus {
|
||||
border: 1px solid var(--menu-border-color-focus);
|
||||
background-color: var(--menu-background-color-focus);
|
||||
--context-menu-item-bg-color: var(--menu-background-color-focus);
|
||||
--context-menu-item-border-color: var(--color-background-tertiary);
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
pointer-events: none;
|
||||
opacity: 0.6;
|
||||
&[aria-selected="true"] {
|
||||
--context-menu-item-bg-color: var(--color-background-quaternary);
|
||||
}
|
||||
}
|
||||
|
||||
.item-text {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.item-with-icon-space {
|
||||
padding-left: $s-20;
|
||||
}
|
||||
.icon-wrapper {
|
||||
margin-right: $s-4;
|
||||
}
|
||||
|
|
|
@ -79,9 +79,10 @@
|
|||
(fn [event token]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dt/show-token-context-menu {:type :token
|
||||
:position (dom/get-client-position event)
|
||||
:token-name (:name token)}))))
|
||||
(st/emit! (dt/show-token-context-menu
|
||||
{:type :token
|
||||
:position (dom/get-client-position event)
|
||||
:token-name (:name token)}))))
|
||||
|
||||
on-toggle-open-click (mf/use-fn
|
||||
(mf/deps open? tokens)
|
||||
|
@ -249,7 +250,8 @@
|
|||
[:& token-context-menu]
|
||||
[:& title-bar {:all-clickable true
|
||||
:title "TOKENS"}]
|
||||
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups) (:empty token-groups))]
|
||||
(for [{:keys [token-key token-type-props tokens]} (concat (:filled token-groups)
|
||||
(:empty token-groups))]
|
||||
[:& token-component {:key token-key
|
||||
:type token-key
|
||||
:selected-shapes selected-shapes
|
||||
|
@ -276,6 +278,10 @@
|
|||
(reset! show-menu* false)))
|
||||
|
||||
input-ref (mf/use-ref)
|
||||
on-option-click
|
||||
(mf/use-fn
|
||||
#(.click (mf/ref-val input-ref)))
|
||||
|
||||
on-import
|
||||
(fn [event]
|
||||
(let [file (-> event .-target .-files (aget 0))]
|
||||
|
@ -313,7 +319,7 @@
|
|||
:on-close close-menu
|
||||
:list-class (stl/css :import-export-menu)}
|
||||
[:> dropdown-menu-item* {:class (stl/css :import-export-menu-item)
|
||||
:on-click #(.click (mf/ref-val input-ref))}
|
||||
:on-click on-option-click}
|
||||
(tr "labels.import")]
|
||||
|
||||
[:> dropdown-menu-item* {:class (stl/css :import-export-menu-item)
|
||||
|
|
|
@ -39,8 +39,8 @@
|
|||
}
|
||||
|
||||
.themes-header {
|
||||
@include use-typography("headline-small");
|
||||
display: block;
|
||||
@include headlineSmallTypography;
|
||||
margin-bottom: $s-8;
|
||||
padding-left: $s-8;
|
||||
color: var(--title-foreground-color);
|
||||
|
@ -105,6 +105,19 @@
|
|||
box-shadow: var(--el-shadow-dark);
|
||||
}
|
||||
|
||||
.import-export-button {
|
||||
@extend .button-secondary;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: end;
|
||||
padding: $s-6 $s-8;
|
||||
text-transform: uppercase;
|
||||
gap: $s-8;
|
||||
background-color: var(--color-background-primary);
|
||||
|
||||
box-shadow: var(--el-shadow-dark);
|
||||
}
|
||||
|
||||
.import-export-menu {
|
||||
@extend .menu-dropdown;
|
||||
top: -#{$s-6};
|
||||
|
@ -117,23 +130,8 @@
|
|||
.import-export-menu-item {
|
||||
@extend .menu-item-base;
|
||||
cursor: pointer;
|
||||
.open-arrow {
|
||||
@include flexCenter;
|
||||
svg {
|
||||
@extend .button-icon;
|
||||
stroke: var(--icon-foreground);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
color: var(--menu-foreground-color-hover);
|
||||
.open-arrow {
|
||||
svg {
|
||||
stroke: var(--menu-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
.shortcut-key {
|
||||
color: var(--menu-shortcut-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6742,6 +6742,18 @@ msgstr "There are no sets yet."
|
|||
msgid "workspace.token.no-sets-create"
|
||||
msgstr "There are no sets defined yet. Create one first."
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/context_menu.cljs
|
||||
msgid "workspace.token.delete"
|
||||
msgstr "Delete token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/context_menu.cljs
|
||||
msgid "workspace.token.duplicate"
|
||||
msgstr "Duplicate token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/context_menu.cljs
|
||||
msgid "workspace.token.edit"
|
||||
msgstr "Edit token"
|
||||
|
||||
msgid "workspace.versions.button.save"
|
||||
msgstr "Save version"
|
||||
|
||||
|
|
|
@ -6746,6 +6746,18 @@ msgstr "Crea uno."
|
|||
msgid "workspace.token.no-sets-create"
|
||||
msgstr "Aun no hay sets definidos. Crea uno primero"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/context_menu.cljs
|
||||
msgid "workspace.token.delete"
|
||||
msgstr "Eliminar token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/context_menu.cljs
|
||||
msgid "workspace.token.duplicate"
|
||||
msgstr "Duplicar token"
|
||||
|
||||
#: src/app/main/ui/workspace/tokens/context_menu.cljs
|
||||
msgid "workspace.token.edit"
|
||||
msgstr "Editar token"
|
||||
|
||||
msgid "workspace.versions.button.save"
|
||||
msgstr "Guardar versión"
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue