mirror of
https://github.com/penpot/penpot.git
synced 2025-12-12 06:24:17 +01:00
✨ Add export tokens modal with multi-file export (#6649)
This commit is contained in:
@@ -28,6 +28,7 @@ on [its own changelog](library/CHANGES.md)
|
|||||||
- Support system color scheme [Github #5030](https://github.com/penpot/penpot/issues/5030)
|
- Support system color scheme [Github #5030](https://github.com/penpot/penpot/issues/5030)
|
||||||
- Persist ruler visibility across files and reloads [GitHub #4586](https://github.com/penpot/penpot/issues/4586)
|
- Persist ruler visibility across files and reloads [GitHub #4586](https://github.com/penpot/penpot/issues/4586)
|
||||||
- Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792)
|
- Update google fonts (at 2025/05/19) [Taiga 10792](https://tree.taiga.io/project/penpot/us/10792)
|
||||||
|
- Adds tokens multi file export [Github #117](https://github.com/tokens-studio/penpot/issues/117)
|
||||||
|
|
||||||
### :bug: Bugs fixed
|
### :bug: Bugs fixed
|
||||||
- Fix getCurrentUser for plugins api [Taiga #11057](https://tree.taiga.io/project/penpot/issue/11057)
|
- Fix getCurrentUser for plugins api [Taiga #11057](https://tree.taiga.io/project/penpot/issue/11057)
|
||||||
|
|||||||
@@ -98,7 +98,7 @@
|
|||||||
(defn encode
|
(defn encode
|
||||||
[data & {:as opts}]
|
[data & {:as opts}]
|
||||||
#?(:clj (j/write-str data opts)
|
#?(:clj (j/write-str data opts)
|
||||||
:cljs (.stringify js/JSON (->js data opts))))
|
:cljs (.stringify js/JSON (->js data opts) nil (:indent opts))))
|
||||||
|
|
||||||
(defn decode
|
(defn decode
|
||||||
[data & {:as opts}]
|
[data & {:as opts}]
|
||||||
|
|||||||
@@ -1420,8 +1420,13 @@ Will return a value that matches this schema:
|
|||||||
:else
|
:else
|
||||||
(parse-multi-set-dtcg-json decoded-json))))
|
(parse-multi-set-dtcg-json decoded-json))))
|
||||||
|
|
||||||
(defn export-dtcg-json
|
(defn- token->dtcg-token [token]
|
||||||
"Convert a TokensLib into a plain clojure map, suitable to be encoded as a multi sets json string in DTCG format."
|
(cond-> {"$value" (:value token)
|
||||||
|
"$type" (cto/token-type->dtcg-token-type (:type token))}
|
||||||
|
(:description token) (assoc "$description" (:description token))))
|
||||||
|
|
||||||
|
(defn- dtcg-export-themes
|
||||||
|
"Extract themes for a dtcg json export."
|
||||||
[tokens-lib]
|
[tokens-lib]
|
||||||
(let [themes-xform
|
(let [themes-xform
|
||||||
(comp
|
(comp
|
||||||
@@ -1443,22 +1448,37 @@ Will return a value that matches this schema:
|
|||||||
(into [] themes-xform))
|
(into [] themes-xform))
|
||||||
|
|
||||||
;; Active themes without exposing hidden penpot theme
|
;; Active themes without exposing hidden penpot theme
|
||||||
active-themes-clear
|
active-themes
|
||||||
(-> (get-active-theme-paths tokens-lib)
|
(-> (get-active-theme-paths tokens-lib)
|
||||||
(disj hidden-theme-path))
|
(disj hidden-theme-path))]
|
||||||
|
{:themes themes
|
||||||
|
:active-themes active-themes}))
|
||||||
|
|
||||||
update-token-fn
|
(defn export-dtcg-multi-file
|
||||||
(fn [token]
|
"Convert a TokensLib into a plain clojure map, suitable to be encoded as a multi json files each encoded in DTCG format."
|
||||||
(cond-> {"$value" (:value token)
|
[tokens-lib]
|
||||||
"$type" (cto/token-type->dtcg-token-type (:type token))}
|
(let [{:keys [themes active-themes]} (dtcg-export-themes tokens-lib)
|
||||||
(:description token) (assoc "$description" (:description token))))
|
sets (->> (get-sets tokens-lib)
|
||||||
|
(map (fn [{:keys [name tokens]}]
|
||||||
|
[(str name ".json") (tokens-tree tokens :update-token-fn token->dtcg-token)]))
|
||||||
|
(into {}))]
|
||||||
|
(-> sets
|
||||||
|
(assoc "$themes.json" themes)
|
||||||
|
(assoc "$metadata.json" {"tokenSetOrder" (get-ordered-set-names tokens-lib)
|
||||||
|
"activeThemes" active-themes
|
||||||
|
"activeSets" (get-active-themes-set-names tokens-lib)}))))
|
||||||
|
|
||||||
|
(defn export-dtcg-json
|
||||||
|
"Convert a TokensLib into a plain clojure map, suitable to be encoded as a multi sets json string in DTCG format."
|
||||||
|
[tokens-lib]
|
||||||
|
(let [{:keys [themes active-themes]} (dtcg-export-themes tokens-lib)
|
||||||
|
|
||||||
name-set-tuples
|
name-set-tuples
|
||||||
(->> (get-set-tree tokens-lib)
|
(->> (get-set-tree tokens-lib)
|
||||||
(tree-seq d/ordered-map? vals)
|
(tree-seq d/ordered-map? vals)
|
||||||
(filter (partial instance? TokenSet))
|
(filter (partial instance? TokenSet))
|
||||||
(map (fn [{:keys [name tokens]}]
|
(map (fn [{:keys [name tokens]}]
|
||||||
[name (tokens-tree tokens :update-token-fn update-token-fn)])))
|
[name (tokens-tree tokens :update-token-fn token->dtcg-token)])))
|
||||||
|
|
||||||
ordered-set-names
|
ordered-set-names
|
||||||
(mapv first name-set-tuples)
|
(mapv first name-set-tuples)
|
||||||
@@ -1471,9 +1491,9 @@ Will return a value that matches this schema:
|
|||||||
|
|
||||||
(-> sets
|
(-> sets
|
||||||
(assoc "$themes" themes)
|
(assoc "$themes" themes)
|
||||||
(assoc-in ["$metadata" "tokenSetOrder"] ordered-set-names)
|
(assoc "$metadata" {"tokenSetOrder" ordered-set-names
|
||||||
(assoc-in ["$metadata" "activeThemes"] active-themes-clear)
|
"activeThemes" active-themes
|
||||||
(assoc-in ["$metadata" "activeSets"] active-set-names))))
|
"activeSets" active-set-names}))))
|
||||||
|
|
||||||
(defn get-tokens-of-unknown-type
|
(defn get-tokens-of-unknown-type
|
||||||
"Search for all tokens in the decoded json file that have a type that is not currently
|
"Search for all tokens in the decoded json file that have a type that is not currently
|
||||||
|
|||||||
@@ -1507,3 +1507,56 @@
|
|||||||
"$type" "color"
|
"$type" "color"
|
||||||
"$description" ""}}}}}]
|
"$description" ""}}}}}]
|
||||||
(t/is (= expected result)))))
|
(t/is (= expected result)))))
|
||||||
|
|
||||||
|
#?(:clj
|
||||||
|
(t/deftest export-dtcg-multi-file
|
||||||
|
(let [now (dt/now)
|
||||||
|
tokens-lib (-> (ctob/make-tokens-lib)
|
||||||
|
(ctob/add-set (ctob/make-token-set :name "some/set"
|
||||||
|
:tokens {"colors.red.600"
|
||||||
|
(ctob/make-token
|
||||||
|
{:name "colors.red.600"
|
||||||
|
:type :color
|
||||||
|
:value "#e53e3e"})
|
||||||
|
"spacing.multi-value"
|
||||||
|
(ctob/make-token
|
||||||
|
{:name "spacing.multi-value"
|
||||||
|
:type :spacing
|
||||||
|
:value "{dimension.sm} {dimension.xl}"
|
||||||
|
:description "You can have multiple values in a single spacing token"})
|
||||||
|
"button.primary.background"
|
||||||
|
(ctob/make-token
|
||||||
|
{:name "button.primary.background"
|
||||||
|
:type :color
|
||||||
|
:value "{accent.default}"})}))
|
||||||
|
(ctob/add-theme (ctob/make-token-theme :name "theme-1"
|
||||||
|
:group "group-1"
|
||||||
|
:external-id "test-id-01"
|
||||||
|
:modified-at now
|
||||||
|
:sets #{"some/set"}))
|
||||||
|
(ctob/toggle-theme-active? "group-1" "theme-1"))
|
||||||
|
result (ctob/export-dtcg-multi-file tokens-lib)
|
||||||
|
expected {"$themes.json" [{"description" ""
|
||||||
|
"group" "group-1"
|
||||||
|
"is-source" false
|
||||||
|
"modified-at" now
|
||||||
|
"id" "test-id-01"
|
||||||
|
"name" "theme-1"
|
||||||
|
"selectedTokenSets" {"some/set" "enabled"}}]
|
||||||
|
"$metadata.json" {"tokenSetOrder" ["some/set"]
|
||||||
|
"activeThemes" #{"group-1/theme-1"}
|
||||||
|
"activeSets" #{"some/set"}}
|
||||||
|
"some/set.json"
|
||||||
|
{"colors" {"red" {"600" {"$value" "#e53e3e"
|
||||||
|
"$type" "color"
|
||||||
|
"$description" ""}}}
|
||||||
|
"spacing"
|
||||||
|
{"multi-value"
|
||||||
|
{"$value" "{dimension.sm} {dimension.xl}"
|
||||||
|
"$type" "spacing"
|
||||||
|
"$description" "You can have multiple values in a single spacing token"}}
|
||||||
|
"button"
|
||||||
|
{"primary" {"background" {"$value" "{accent.default}"
|
||||||
|
"$type" "color"
|
||||||
|
"$description" ""}}}}}]
|
||||||
|
(t/is (= expected result)))))
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
[app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]]
|
[app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button]]
|
||||||
[app.main.ui.workspace.sidebar.history :refer [history-toolbox*]]
|
[app.main.ui.workspace.sidebar.history :refer [history-toolbox*]]
|
||||||
[app.main.ui.workspace.tokens.modals]
|
[app.main.ui.workspace.tokens.modals]
|
||||||
|
[app.main.ui.workspace.tokens.modals.export]
|
||||||
[app.main.ui.workspace.tokens.modals.import]
|
[app.main.ui.workspace.tokens.modals.import]
|
||||||
[app.main.ui.workspace.tokens.modals.settings]
|
[app.main.ui.workspace.tokens.modals.settings]
|
||||||
[app.main.ui.workspace.tokens.modals.themes]
|
[app.main.ui.workspace.tokens.modals.themes]
|
||||||
|
|||||||
144
frontend/src/app/main/ui/workspace/tokens/modals/export.cljs
Normal file
144
frontend/src/app/main/ui/workspace/tokens/modals/export.cljs
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
;; 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
|
||||||
|
|
||||||
|
(ns app.main.ui.workspace.tokens.modals.export
|
||||||
|
(:require-macros [app.main.style :as stl])
|
||||||
|
(:require
|
||||||
|
[app.common.json :as json]
|
||||||
|
[app.common.types.tokens-lib :as ctob]
|
||||||
|
[app.main.data.modal :as modal]
|
||||||
|
[app.main.refs :as refs]
|
||||||
|
[app.main.ui.components.code-block :refer [code-block]]
|
||||||
|
[app.main.ui.ds.buttons.button :refer [button*]]
|
||||||
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
||||||
|
[app.main.ui.ds.foundations.assets.icon :refer [icon*]]
|
||||||
|
[app.main.ui.ds.foundations.typography.heading :refer [heading*]]
|
||||||
|
[app.main.ui.ds.foundations.typography.text :refer [text*]]
|
||||||
|
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
||||||
|
[app.util.dom :as dom]
|
||||||
|
[app.util.i18n :refer [tr]]
|
||||||
|
[app.util.webapi :as wapi]
|
||||||
|
[app.util.zip :as zip]
|
||||||
|
[rumext.v2 :as mf]))
|
||||||
|
|
||||||
|
(mf/defc export-tab*
|
||||||
|
{::mf/private true}
|
||||||
|
[{:keys [on-export is-disabled children]}]
|
||||||
|
[:div {:class (stl/css :export-preview)}
|
||||||
|
(when-not is-disabled
|
||||||
|
[:> text* {:as "span" :typography "body-medium" :class (stl/css :preview-label)}
|
||||||
|
(tr "workspace.tokens.export.preview")])
|
||||||
|
(if is-disabled
|
||||||
|
[:div {:class (stl/css :disabled-message)}
|
||||||
|
(tr "workspace.tokens.export.no-tokens-themes-sets")]
|
||||||
|
children)
|
||||||
|
[:div {:class (stl/css :export-actions)}
|
||||||
|
[:> button* {:variant "secondary"
|
||||||
|
:type "button"
|
||||||
|
:on-click modal/hide!}
|
||||||
|
(tr "labels.cancel")]
|
||||||
|
[:> button* {:variant "primary"
|
||||||
|
:type "button"
|
||||||
|
:disabled is-disabled
|
||||||
|
:on-click on-export}
|
||||||
|
(tr "workspace.tokens.export")]]])
|
||||||
|
|
||||||
|
(mf/defc single-file-tab*
|
||||||
|
{::mf/private true}
|
||||||
|
[]
|
||||||
|
(let [tokens-data (some-> (deref refs/tokens-lib)
|
||||||
|
(ctob/export-dtcg-json))
|
||||||
|
tokens-json (some-> tokens-data
|
||||||
|
(json/encode :key-fn identity :indent 2))
|
||||||
|
is-disabled (empty? tokens-data)
|
||||||
|
on-export
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps tokens-json)
|
||||||
|
(fn []
|
||||||
|
(when tokens-json
|
||||||
|
(->> (wapi/create-blob (or tokens-json "{}") "application/json")
|
||||||
|
(dom/trigger-download "tokens.json")))))]
|
||||||
|
[:> export-tab* {:is-disabled is-disabled
|
||||||
|
:on-export on-export}
|
||||||
|
[:div {:class (stl/css :json-preview)}
|
||||||
|
[:> code-block {:code tokens-json :type "json"}]]]))
|
||||||
|
|
||||||
|
(defn download-tokens-zip! [multi-file-entries]
|
||||||
|
(let [writer (-> (zip/blob-writer {:mtype "application/zip"})
|
||||||
|
(zip/writer))]
|
||||||
|
(doseq [[path content] multi-file-entries]
|
||||||
|
(zip/add writer path (json/encode content :key-fn identity :indent 2)))
|
||||||
|
(-> (zip/close writer)
|
||||||
|
(.then #(dom/trigger-download "tokens.zip" %)))))
|
||||||
|
|
||||||
|
(mf/defc multi-file-tab*
|
||||||
|
{::mf/private true}
|
||||||
|
[]
|
||||||
|
(let [files (some->> (deref refs/tokens-lib)
|
||||||
|
(ctob/export-dtcg-multi-file))
|
||||||
|
is-disabled (or (empty? files)
|
||||||
|
(every? (fn [[_ v]] (empty? v)) files))
|
||||||
|
on-export
|
||||||
|
(mf/use-fn
|
||||||
|
(mf/deps files)
|
||||||
|
(fn []
|
||||||
|
(download-tokens-zip! files)))]
|
||||||
|
[:> export-tab* {:on-export on-export
|
||||||
|
:is-disabled is-disabled}
|
||||||
|
[:div {:class (stl/css :preview-container)}
|
||||||
|
[:ul {:class (stl/css :file-list)}
|
||||||
|
(for [[path] files]
|
||||||
|
[:li {:key path
|
||||||
|
:class (stl/css :file-item)}
|
||||||
|
[:div {:class (stl/css :file-icon)}
|
||||||
|
[:> icon* {:icon-id "document"}]]
|
||||||
|
[:div {:class (stl/css :file-name) :title path}
|
||||||
|
path]])]]]))
|
||||||
|
|
||||||
|
(mf/defc export-modal-body*
|
||||||
|
{::mf/private true}
|
||||||
|
[]
|
||||||
|
(let [selected-tab (mf/use-state "single-file")
|
||||||
|
|
||||||
|
on-change-tab
|
||||||
|
(mf/use-fn
|
||||||
|
(fn [tab-id]
|
||||||
|
(reset! selected-tab tab-id)))
|
||||||
|
|
||||||
|
single-file-content
|
||||||
|
(mf/html [:> single-file-tab*])
|
||||||
|
|
||||||
|
multiple-files-content
|
||||||
|
(mf/html [:> multi-file-tab*])
|
||||||
|
|
||||||
|
tabs #js [#js {:label (tr "workspace.tokens.export.single-file")
|
||||||
|
:id "single-file"
|
||||||
|
:content single-file-content}
|
||||||
|
#js {:label (tr "workspace.tokens.export.multiple-files")
|
||||||
|
:id "multiple-files"
|
||||||
|
:content multiple-files-content}]]
|
||||||
|
|
||||||
|
[:div {:class (stl/css :export-modal-wrapper)}
|
||||||
|
[:> heading* {:level 2 :typography "headline-medium" :class (stl/css :export-modal-title)}
|
||||||
|
(tr "workspace.tokens.export-tokens")]
|
||||||
|
|
||||||
|
[:> tab-switcher*
|
||||||
|
{:tabs tabs
|
||||||
|
:selected @selected-tab
|
||||||
|
:on-change-tab on-change-tab}]]))
|
||||||
|
|
||||||
|
(mf/defc export-modal*
|
||||||
|
{::mf/register modal/components
|
||||||
|
::mf/register-as :tokens/export}
|
||||||
|
[]
|
||||||
|
[:div {:class (stl/css :modal-overlay)}
|
||||||
|
[:div {:class (stl/css :modal-dialog)}
|
||||||
|
[:> icon-button* {:class (stl/css :close-btn)
|
||||||
|
:on-click modal/hide!
|
||||||
|
:aria-label (tr "labels.close")
|
||||||
|
:variant "ghost"
|
||||||
|
:icon "close"}]
|
||||||
|
[:> export-modal-body*]]])
|
||||||
125
frontend/src/app/main/ui/workspace/tokens/modals/export.scss
Normal file
125
frontend/src/app/main/ui/workspace/tokens/modals/export.scss
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
//
|
||||||
|
// Copyright (c) KALEIDOS INC
|
||||||
|
|
||||||
|
@use "../../../ds/typography.scss" as t;
|
||||||
|
@use "../../../ds/_sizes.scss" as *;
|
||||||
|
@use "../../../ds/_borders.scss" as *;
|
||||||
|
@import "refactor/common-refactor.scss";
|
||||||
|
|
||||||
|
.modal-overlay {
|
||||||
|
@extend .modal-overlay-base;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-dialog {
|
||||||
|
--modal-width: 32rem;
|
||||||
|
--modal-padding: var(--sp-xxxl);
|
||||||
|
--container-max-height: 16rem;
|
||||||
|
@extend .modal-container-base;
|
||||||
|
user-select: none;
|
||||||
|
width: var(--modal-width);
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-modal-wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--sp-xxl);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-modal-title {
|
||||||
|
color: var(--color-foreground-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-preview {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--sp-m);
|
||||||
|
padding-top: var(--sp-m);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-label {
|
||||||
|
color: var(--color-foreground-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.preview-container {
|
||||||
|
border: $b-1 solid var(--color-background-quaternary);
|
||||||
|
border-radius: $br-8;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--sp-xs) var(--sp-m);
|
||||||
|
max-height: var(--container-max-height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-list {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
cursor: default;
|
||||||
|
color: var(--color-foreground-secondary);
|
||||||
|
border: $br-2 solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
@include textEllipsis;
|
||||||
|
@include t.use-typography("body-medium");
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: var(--sp-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.export-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: var(--sp-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-btn {
|
||||||
|
position: absolute;
|
||||||
|
inset-block-start: var(--sp-s);
|
||||||
|
inset-inline-end: var(--sp-s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-preview {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-preview pre {
|
||||||
|
border: $b-1 solid var(--color-background-quaternary);
|
||||||
|
border-radius: $br-8;
|
||||||
|
margin: 0;
|
||||||
|
max-height: var(--container-max-height);
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: auto;
|
||||||
|
word-wrap: normal;
|
||||||
|
white-space: pre;
|
||||||
|
max-width: calc(var(--modal-width) - var(--modal-padding) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.disabled-message {
|
||||||
|
@include t.use-typography("body-small");
|
||||||
|
color: var(--color-foreground-secondary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border: $b-1 solid var(--color-background-quaternary);
|
||||||
|
border-radius: $br-8;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--sp-s) var(--sp-m);
|
||||||
|
max-height: var(--container-max-height);
|
||||||
|
}
|
||||||
@@ -8,7 +8,6 @@
|
|||||||
(:require-macros [app.main.style :as stl])
|
(:require-macros [app.main.style :as stl])
|
||||||
(:require
|
(:require
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.json :as json]
|
|
||||||
[app.common.types.tokens-lib :as ctob]
|
[app.common.types.tokens-lib :as ctob]
|
||||||
[app.config :as cf]
|
[app.config :as cf]
|
||||||
[app.main.data.event :as ev]
|
[app.main.data.event :as ev]
|
||||||
@@ -37,7 +36,6 @@
|
|||||||
[app.util.array :as array]
|
[app.util.array :as array]
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.i18n :refer [tr]]
|
[app.util.i18n :refer [tr]]
|
||||||
[app.util.webapi :as wapi]
|
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[potok.v2.core :as ptk]
|
[potok.v2.core :as ptk]
|
||||||
[rumext.v2 :as mf]
|
[rumext.v2 :as mf]
|
||||||
@@ -387,11 +385,7 @@
|
|||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
(fn []
|
(fn []
|
||||||
(st/emit! (ptk/data-event ::ev/event {::ev/name "export-tokens"}))
|
(st/emit! (ptk/data-event ::ev/event {::ev/name "export-tokens"}))
|
||||||
(let [tokens-json (some-> (deref refs/tokens-lib)
|
(modal/show! :tokens/export {})))
|
||||||
(ctob/export-dtcg-json)
|
|
||||||
(json/encode :key-fn identity))]
|
|
||||||
(->> (wapi/create-blob (or tokens-json "{}") "application/json")
|
|
||||||
(dom/trigger-download "tokens.json")))))
|
|
||||||
|
|
||||||
on-modal-show
|
on-modal-show
|
||||||
(mf/use-fn
|
(mf/use-fn
|
||||||
|
|||||||
@@ -7249,6 +7249,30 @@ msgstr "Importing a JSON file will override all your current tokens, sets and th
|
|||||||
msgid "workspace.tokens.import-warning"
|
msgid "workspace.tokens.import-warning"
|
||||||
msgstr "Importing tokens will override all your current tokens, sets and themes."
|
msgstr "Importing tokens will override all your current tokens, sets and themes."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:74
|
||||||
|
msgid "workspace.tokens.export"
|
||||||
|
msgstr "Export"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:47
|
||||||
|
msgid "workspace.tokens.export-tokens"
|
||||||
|
msgstr "Export tokens"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:51
|
||||||
|
msgid "workspace.tokens.export.single-file"
|
||||||
|
msgstr "Single file"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:54
|
||||||
|
msgid "workspace.tokens.export.multiple-files"
|
||||||
|
msgstr "Multiple files"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:60
|
||||||
|
msgid "workspace.tokens.export.preview"
|
||||||
|
msgstr "Preview:"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:37
|
||||||
|
msgid "workspace.tokens.export.no-tokens-themes-sets"
|
||||||
|
msgstr "There are no tokens, themes or sets to export."
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs:341
|
#: src/app/main/ui/workspace/tokens/sidebar.cljs:341
|
||||||
msgid "workspace.tokens.inactive-set"
|
msgid "workspace.tokens.inactive-set"
|
||||||
msgstr "Inactive"
|
msgstr "Inactive"
|
||||||
|
|||||||
@@ -7265,6 +7265,26 @@ msgstr "Al importar un fichero JSON sobreescribirás todos tus tokens, sets y th
|
|||||||
msgid "workspace.tokens.import-warning"
|
msgid "workspace.tokens.import-warning"
|
||||||
msgstr "Al importar tokens sobreescribirás todos tus tokens, sets y themes."
|
msgstr "Al importar tokens sobreescribirás todos tus tokens, sets y themes."
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:74
|
||||||
|
msgid "workspace.tokens.export"
|
||||||
|
msgstr "Exportar"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:51
|
||||||
|
msgid "workspace.tokens.export.single-file"
|
||||||
|
msgstr "fichero único"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:54
|
||||||
|
msgid "workspace.tokens.export.multiple-files"
|
||||||
|
msgstr "Múltiples ficheros"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:60
|
||||||
|
msgid "workspace.tokens.export.preview"
|
||||||
|
msgstr "Previsualizar:"
|
||||||
|
|
||||||
|
#: src/app/main/ui/workspace/tokens/modals/export.cljs:37
|
||||||
|
msgid "workspace.tokens.export.no-tokens-themes-sets"
|
||||||
|
msgstr "No existen tokens, temas o sets para exportar."
|
||||||
|
|
||||||
#: src/app/main/ui/workspace/tokens/sidebar.cljs:341
|
#: src/app/main/ui/workspace/tokens/sidebar.cljs:341
|
||||||
msgid "workspace.tokens.inactive-set"
|
msgid "workspace.tokens.inactive-set"
|
||||||
msgstr "Inactivo"
|
msgstr "Inactivo"
|
||||||
|
|||||||
Reference in New Issue
Block a user