mirror of
https://github.com/penpot/penpot.git
synced 2026-06-05 23:20:19 +00:00
Add the option to import tokens from a linked library. I know there are plans to link the tokens in together with the library. Once this happens this patch can be reverted. Until then it helps a lot to use a design system that relies on themes. Before that someones would need to: * Download the design system / add to their team. * Open the file, download the tokens. For every new file: * Link the Design System library. * Import the tokens file. With this patch all you need to get started is to download the design system and add to your team. From their importing the links is done on the same pop-up that is used to import the tokens. --- Technical considerations: I try adding this as a dialog that is called once the library is imported. I ran into a few issues though: * To find whether the library has tokens (and thus show the dialog) I would need to extend library summary to include tokens. * I couldn't find a reliable way to import the tokens after importing the library without resorting to a timer :/ I'm sure both of those hurdles are doable, I just wasted enough time trying it to the point I decided on a different approach. Signed-off-by: Dalai Felinto <dalai@blender.org> 📎 Fix minor issues and linter reports 📎 Reuse translations
765 lines
31 KiB
Clojure
765 lines
31 KiB
Clojure
;; 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.libraries
|
|
(:require-macros [app.main.style :as stl])
|
|
(:require
|
|
[app.common.data :as d]
|
|
[app.common.data.macros :as dm]
|
|
[app.common.files.variant :as cfv]
|
|
[app.common.types.components-list :as ctkl]
|
|
[app.common.types.file :as ctf]
|
|
[app.common.types.library :as ctl]
|
|
[app.common.types.tokens-lib :as ctob]
|
|
[app.common.types.typographies-list :as ctyl]
|
|
[app.common.uuid :as uuid]
|
|
[app.config :as cf]
|
|
[app.main.data.dashboard :as dd]
|
|
[app.main.data.modal :as modal]
|
|
[app.main.data.notifications :as ntf]
|
|
[app.main.data.profile :as du]
|
|
[app.main.data.team :as dtm]
|
|
[app.main.data.workspace.colors :as mdc]
|
|
[app.main.data.workspace.libraries :as dwl]
|
|
[app.main.refs :as refs]
|
|
[app.main.render :refer [component-svg]]
|
|
[app.main.store :as st]
|
|
[app.main.ui.components.color-bullet :as cb]
|
|
[app.main.ui.components.link-button :as lb]
|
|
[app.main.ui.components.search-bar :refer [search-bar*]]
|
|
[app.main.ui.components.title-bar :refer [title-bar*]]
|
|
[app.main.ui.context :as ctx]
|
|
[app.main.ui.ds.buttons.icon-button :refer [icon-button*]]
|
|
[app.main.ui.ds.foundations.assets.icon :as i]
|
|
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
|
|
[app.main.ui.ds.product.empty-state :refer [empty-state*]]
|
|
[app.main.ui.hooks :as h]
|
|
[app.main.ui.icons :as deprecated-icon]
|
|
[app.main.ui.workspace.tokens.import-from-library]
|
|
[app.util.color :as uc]
|
|
[app.util.dom :as dom]
|
|
[app.util.i18n :refer [c tr]]
|
|
[app.util.strings :refer [matches-search]]
|
|
[beicon.v2.core :as rx]
|
|
[cuerdas.core :as str]
|
|
[rumext.v2 :as mf]))
|
|
|
|
(def ^:private close-icon
|
|
(deprecated-icon/icon-xref :close (stl/css :close-icon)))
|
|
|
|
(def ^:private add-icon
|
|
(deprecated-icon/icon-xref :add (stl/css :add-icon)))
|
|
|
|
(defn- get-library-summary
|
|
"Given a library data return a summary representation of this library"
|
|
[data]
|
|
(let [colors (count (:colors data))
|
|
graphics 0
|
|
typographies (count (:typographies data))
|
|
components (count (->> (ctkl/components-seq data)
|
|
(remove #(cfv/is-secondary-variant? % data))))
|
|
empty? (and (zero? components)
|
|
(zero? graphics)
|
|
(zero? colors)
|
|
(zero? typographies))]
|
|
|
|
{:is-empty empty?
|
|
:colors colors
|
|
:graphics graphics
|
|
:typographies typographies
|
|
:components components}))
|
|
|
|
(defn- adapt-backend-summary
|
|
[summary]
|
|
(let [components (or (-> summary :components :count) 0)
|
|
graphics (or (-> summary :media :count) 0)
|
|
typographies (or (-> summary :typographies :count) 0)
|
|
colors (or (-> summary :colors :count) 0)
|
|
|
|
empty? (and (zero? components)
|
|
(zero? graphics)
|
|
(zero? colors)
|
|
(zero? typographies))]
|
|
{:is-empty empty?
|
|
:components components
|
|
:graphics graphics
|
|
:typographies typographies
|
|
:colors colors}))
|
|
|
|
(defn- describe-library
|
|
[components-count graphics-count colors-count typography-count]
|
|
(let [all-zero? (and (zero? components-count)
|
|
(zero? graphics-count)
|
|
(zero? colors-count)
|
|
(zero? typography-count))]
|
|
(str
|
|
(str/join " · "
|
|
(cond-> []
|
|
(or all-zero? (pos? components-count))
|
|
(conj (tr "workspace.libraries.components" (c components-count)))
|
|
|
|
(or all-zero? (pos? graphics-count))
|
|
(conj (tr "workspace.libraries.graphics" (c graphics-count)))
|
|
|
|
(or all-zero? (pos? colors-count))
|
|
(conj (tr "workspace.libraries.colors" (c colors-count)))
|
|
|
|
(or all-zero? (pos? typography-count))
|
|
(conj (tr "workspace.libraries.typography" (c typography-count)))))
|
|
"\u00A0")))
|
|
|
|
(mf/defc library-description*
|
|
{::mf/props :obj
|
|
::mf/private true}
|
|
[{:keys [summary]}]
|
|
(let [components-count (get summary :components)
|
|
graphics-count (get summary :graphics)
|
|
typography-count (get summary :typographies)
|
|
colors-count (get summary :colors)]
|
|
|
|
[:*
|
|
(when (pos? components-count)
|
|
[:li {:class (stl/css :element-count)}
|
|
(tr "workspace.libraries.components" (c components-count))])
|
|
|
|
(when (pos? graphics-count)
|
|
[:li {:class (stl/css :element-count)}
|
|
(tr "workspace.libraries.graphics" (c graphics-count))])
|
|
|
|
(when (pos? colors-count)
|
|
[:li {:class (stl/css :element-count)}
|
|
(tr "workspace.libraries.colors" (c colors-count))])
|
|
|
|
(when (pos? typography-count)
|
|
[:li {:class (stl/css :element-count)}
|
|
(tr "workspace.libraries.typography" (c typography-count))])]))
|
|
|
|
(mf/defc sample-library-entry*
|
|
{::mf/props :obj
|
|
::mf/private true}
|
|
[{:keys [library importing]}]
|
|
(let [id (:id library)
|
|
importing? (deref importing)
|
|
|
|
team-id (mf/use-ctx ctx/current-team-id)
|
|
|
|
on-error
|
|
(mf/use-fn
|
|
(fn [_]
|
|
(reset! importing nil)
|
|
(rx/of (ntf/error (tr "dashboard.libraries-and-templates.import-error")))))
|
|
|
|
on-success
|
|
(mf/use-fn
|
|
(mf/deps team-id)
|
|
(fn [_]
|
|
(st/emit! (dtm/fetch-shared-files team-id))))
|
|
|
|
import-library
|
|
(mf/use-fn
|
|
(mf/deps on-success on-error)
|
|
(fn [_]
|
|
(reset! importing id)
|
|
(st/emit! (dd/clone-template
|
|
(with-meta {:template-id id}
|
|
{:on-success on-success
|
|
:on-error on-error})))))]
|
|
|
|
[:div {:class (stl/css :sample-library-item)
|
|
:key (dm/str id)}
|
|
[:div {:class (stl/css :sample-library-item-name)} (:name library)]
|
|
[:input {:class (stl/css-case :sample-library-button true
|
|
:sample-library-add (nil? importing?)
|
|
:sample-library-adding (some? importing?))
|
|
:type "button"
|
|
:value (if (= importing? id) (tr "labels.adding") (tr "labels.add"))
|
|
:on-click import-library}]]))
|
|
|
|
(defn- empty-library?
|
|
"Check if currentt library summary has elements or not"
|
|
[summary]
|
|
(boolean (:is-empty summary)))
|
|
|
|
(defn- has-tokens?
|
|
"Check if library has tokens to be imported"
|
|
[{:keys [data]}]
|
|
(when-let [tokens-lib (get data :tokens-lib)]
|
|
(not (ctob/empty-lib? tokens-lib))))
|
|
|
|
(mf/defc libraries-tab*
|
|
{::mf/props :obj
|
|
::mf/private true}
|
|
[{:keys [is-shared linked-libraries shared-libraries]}]
|
|
(let [file-id (mf/use-ctx ctx/current-file-id)
|
|
search-term* (mf/use-state "")
|
|
search-term (deref search-term*)
|
|
|
|
;; The summary of the current/local library
|
|
;; NOTE: we only need a snapshot of current library
|
|
local-library (deref refs/workspace-data)
|
|
summary (get-library-summary local-library)
|
|
empty-library? (empty-library? summary)
|
|
|
|
selected (h/use-shared-state mdc/colorpalette-selected-broadcast-key :recent)
|
|
dependencies (mf/with-memo [shared-libraries]
|
|
(into {} (map (juxt :id :library-file-ids) (vals shared-libraries))))
|
|
|
|
library-names (mf/with-memo [shared-libraries]
|
|
(into {} (map (fn [{:keys [id name]}]
|
|
[id name])
|
|
(vals shared-libraries))))
|
|
|
|
find-connected-to
|
|
(mf/use-fn
|
|
(mf/deps dependencies)
|
|
(fn [library-id]
|
|
(->> dependencies
|
|
(keep (fn [[k v]] (when (contains? v library-id) k))))))
|
|
|
|
shared-libraries
|
|
(mf/with-memo [shared-libraries linked-libraries file-id search-term]
|
|
(when shared-libraries
|
|
(->> (vals shared-libraries)
|
|
(remove #(= (:id %) file-id))
|
|
(remove #(contains? linked-libraries (:id %)))
|
|
(filter #(matches-search (:name %) search-term))
|
|
(map #(assoc % :connected-to (find-connected-to (:id %))))
|
|
(map #(assoc % :connected-to-names (->> (:connected-to %)
|
|
(keep library-names))))
|
|
(sort-by (comp str/lower :name)))))
|
|
|
|
linked-libraries
|
|
(mf/with-memo [linked-libraries find-connected-to library-names]
|
|
(->> (vals linked-libraries)
|
|
(map #(assoc % :connected-to (find-connected-to (:id %))))
|
|
(map #(assoc % :connected-to-names (->> (:connected-to %)
|
|
(keep library-names))))
|
|
(sort-by (comp str/lower :name))))
|
|
|
|
linked-libraries-ids
|
|
(mf/with-memo [linked-libraries]
|
|
(into #{} d/xf:map-id linked-libraries))
|
|
|
|
importing*
|
|
(mf/use-state nil)
|
|
|
|
sample-libraries
|
|
(mf/with-memo []
|
|
[{:id "penpot-design-system", :name "Design system example"}
|
|
{:id "wireframing-kit", :name "Wireframe library"}
|
|
{:id "whiteboarding-kit", :name "Whiteboarding Kit"}])
|
|
|
|
|
|
change-search-term
|
|
(mf/use-fn
|
|
(fn [event]
|
|
(reset! search-term* event)))
|
|
|
|
link-library
|
|
(mf/use-fn
|
|
(mf/deps file-id)
|
|
(fn [event]
|
|
(let [library-id (some-> (dom/get-current-target event)
|
|
(dom/get-data "library-id")
|
|
(uuid/parse))]
|
|
(reset! selected library-id)
|
|
(st/emit! (dwl/link-file-to-library file-id library-id)))))
|
|
|
|
unlink-library
|
|
(mf/use-fn
|
|
(mf/deps file-id)
|
|
(fn [event]
|
|
(let [library-id (some-> (dom/get-current-target event)
|
|
(dom/get-data "library-id")
|
|
(uuid/parse))]
|
|
(when (= library-id @selected)
|
|
(reset! selected :file))
|
|
(st/emit! (dwl/unlink-file-from-library file-id library-id)
|
|
(dwl/sync-file file-id library-id)))))
|
|
|
|
import-tokens
|
|
(mf/use-fn
|
|
(mf/deps file-id)
|
|
(fn [event]
|
|
(let [library-id (some-> (dom/get-current-target event)
|
|
(dom/get-data "library-id")
|
|
(uuid/parse))]
|
|
(st/emit! (modal/show
|
|
:tokens/import-from-library {:file-id file-id
|
|
:library-id library-id})))))
|
|
|
|
on-delete-accept
|
|
(mf/use-fn
|
|
(mf/deps file-id)
|
|
#(st/emit! (dwl/set-file-shared file-id false)
|
|
(modal/show :libraries-dialog {:file-id file-id})))
|
|
|
|
on-delete-cancel
|
|
(mf/use-fn
|
|
(mf/deps file-id)
|
|
#(st/emit! (modal/show :libraries-dialog {:file-id file-id})))
|
|
|
|
publish
|
|
(mf/use-fn
|
|
(mf/deps file-id)
|
|
(fn [event]
|
|
(let [input-node (dom/get-target event)
|
|
publish-library #(st/emit! (dwl/set-file-shared file-id true))
|
|
cancel-publish #(st/emit! (modal/show :libraries-dialog {:file-id file-id}))]
|
|
(if empty-library?
|
|
(st/emit! (modal/show
|
|
{:type :confirm
|
|
:title (tr "modals.publish-empty-library.title")
|
|
:message (tr "modals.publish-empty-library.message")
|
|
:accept-label (tr "modals.publish-empty-library.accept")
|
|
:on-accept publish-library
|
|
:on-cancel cancel-publish}))
|
|
(publish-library))
|
|
(dom/blur! input-node))))
|
|
|
|
unpublish
|
|
(mf/use-fn
|
|
(mf/deps file-id)
|
|
(fn [_]
|
|
(st/emit! (modal/show
|
|
{:type :delete-shared-libraries
|
|
:ids #{file-id}
|
|
:origin :unpublish
|
|
:on-accept on-delete-accept
|
|
:on-cancel on-delete-cancel
|
|
:count-libraries 1}))))]
|
|
|
|
[:div {:class (stl/css :libraries-content)}
|
|
[:div {:class (stl/css :lib-section)}
|
|
[:> title-bar* {:collapsable false
|
|
:title (tr "workspace.libraries.in-this-file")
|
|
:class (stl/css :title-spacing-lib)}]
|
|
[:div {:class (stl/css :section-list)}
|
|
|
|
[:div {:class (stl/css :section-list-item)}
|
|
[:div {:class (stl/css :item-content)}
|
|
[:div {:class (stl/css :item-name)} (tr "workspace.libraries.file-library")]
|
|
[:ul {:class (stl/css :item-contents)}
|
|
[:> library-description* {:summary summary}]]]
|
|
|
|
(if ^boolean is-shared
|
|
[:input {:class (stl/css :item-unpublish)
|
|
:type "button"
|
|
:value (tr "common.unpublish")
|
|
:on-click unpublish}]
|
|
[:input {:class (stl/css :item-publish)
|
|
:type "button"
|
|
:value (tr "common.publish")
|
|
:on-click publish}])]
|
|
|
|
(for [{:keys [id name data connected-to connected-to-names] :as library} linked-libraries]
|
|
(let [disabled? (some #(contains? linked-libraries-ids %) connected-to)
|
|
has-tokens? (and (has-tokens? library)
|
|
(contains? cf/flags :token-import-from-library))]
|
|
[:div {:class (if has-tokens?
|
|
(stl/css :section-list-item-double-icon)
|
|
(stl/css :section-list-item))
|
|
:key (dm/str id)
|
|
:data-testid "library-item"}
|
|
[:div {:class (stl/css :item-content)}
|
|
[:div {:class (stl/css :item-name)} name]
|
|
[:ul {:class (stl/css :item-contents)}
|
|
(let [summary (get-library-summary data)]
|
|
[:*
|
|
[:> library-description* {:summary summary}]
|
|
(when (seq connected-to)
|
|
[:div {:class (stl/css :connected-to-wrapper)}
|
|
[:span "(" (tr "workspace.libraries.connected-to") " "]
|
|
[:span {:class (stl/css :connected-to-values)} (str/join ", " connected-to-names)]
|
|
[:span ")"]])])]]
|
|
|
|
(when ^boolean has-tokens?
|
|
[:> icon-button*
|
|
{:type "button"
|
|
:aria-label (tr "workspace.tokens.import-tokens")
|
|
:icon i/import-export
|
|
:data-library-id (dm/str id)
|
|
:variant "secondary"
|
|
:on-click import-tokens}])
|
|
|
|
[:> icon-button* {:type "button"
|
|
:aria-label (tr "workspace.libraries.unlink-library-btn")
|
|
:icon i/detach
|
|
:data-library-id (dm/str id)
|
|
:variant "secondary"
|
|
:disabled disabled?
|
|
:on-click unlink-library}]]))]]
|
|
|
|
[:div {:class (stl/css :shared-section)}
|
|
[:> title-bar* {:collapsable false
|
|
:title (tr "workspace.libraries.shared-libraries")
|
|
:class (stl/css :title-spacing-lib)}]
|
|
[:> search-bar* {:on-change change-search-term
|
|
:value search-term
|
|
:placeholder (tr "workspace.libraries.search-shared-libraries")
|
|
:icon-id i/search}]
|
|
|
|
(if (seq shared-libraries)
|
|
[:div {:class (stl/css :section-list-shared)}
|
|
(for [{:keys [id name] :as library} shared-libraries]
|
|
[:div {:class (stl/css :section-list-item)
|
|
:key (dm/str id)
|
|
:data-testid "library-item"}
|
|
[:div {:class (stl/css :item-content)}
|
|
[:div {:class (stl/css :item-name)} name]
|
|
[:ul {:class (stl/css :item-contents)}
|
|
(let [summary (-> (:library-summary library)
|
|
(adapt-backend-summary))]
|
|
[:> library-description* {:summary summary}])]]
|
|
|
|
[:button {:class (stl/css :item-button-shared)
|
|
:data-library-id (dm/str id)
|
|
:title (tr "workspace.libraries.shared-library-btn")
|
|
:on-click link-library}
|
|
add-icon]])]
|
|
|
|
(when (empty? shared-libraries)
|
|
[:div {:class (stl/css :section-list-empty)}
|
|
(cond
|
|
(nil? shared-libraries)
|
|
(tr "workspace.libraries.loading")
|
|
|
|
(str/empty? search-term)
|
|
[:*
|
|
[:div {:class (stl/css :sample-libraries-info)}
|
|
(tr "workspace.libraries.empty.no-libraries")
|
|
[:a {:target "_blank"
|
|
:class (stl/css :sample-libraries-link)
|
|
:href "https://penpot.app/libraries-templates"}
|
|
(tr "workspace.libraries.empty.some-templates")]]
|
|
[:div {:class (stl/css :sample-libraries-container)}
|
|
(tr "workspace.libraries.empty.add-some")
|
|
(for [library sample-libraries]
|
|
[:> sample-library-entry*
|
|
{:library library
|
|
:importing importing*}])]]
|
|
|
|
:else
|
|
(tr "workspace.libraries.no-matches-for" search-term))]))]]))
|
|
|
|
(defn- extract-assets
|
|
[file-data library summary?]
|
|
(let [exceeded (volatile! {:components false
|
|
:colors false
|
|
:typographies false})
|
|
|
|
truncate (fn [asset-type items]
|
|
(if (and summary? (> (count items) 5))
|
|
(do
|
|
(vswap! exceeded assoc asset-type true)
|
|
(take 5 items))
|
|
items))
|
|
|
|
assets (dwl/assets-need-sync library file-data)
|
|
|
|
component-ids (into #{} (->> assets
|
|
(filter #(= (:asset-type %) :component))
|
|
(map :asset-id)))
|
|
color-ids (into #{} (->> assets
|
|
(filter #(= (:asset-type %) :color))
|
|
(map :asset-id)))
|
|
typography-ids (into #{} (->> assets
|
|
(filter #(= (:asset-type %) :typography))
|
|
(map :asset-id)))
|
|
|
|
components (->> component-ids
|
|
(map #(ctkl/get-component (:data library) %))
|
|
(sort-by #(str/lower (:name %)))
|
|
(truncate :components))
|
|
colors (->> color-ids
|
|
(map #(ctl/get-color (:data library) %))
|
|
(sort-by #(str/lower (:name %)))
|
|
(truncate :colors))
|
|
typographies (->> typography-ids
|
|
(map #(ctyl/get-typography (:data library) %))
|
|
(sort-by #(str/lower (:name %)))
|
|
(truncate :typographies))]
|
|
|
|
[library @exceeded {:components components
|
|
:colors colors
|
|
:typographies typographies}]))
|
|
|
|
(mf/defc updates-tab*
|
|
{::mf/props :obj
|
|
::mf/private true}
|
|
[{:keys [file-id libraries]}]
|
|
;; FIXME: naming
|
|
(let [summary?* (mf/use-state true)
|
|
summary? (deref summary?*)
|
|
updating? (mf/deref refs/updating-library)
|
|
|
|
;; NOTE: we don't want to react on file changes, we just want
|
|
;; a snapshot of file on the momento of open the dialog
|
|
file-data (deref refs/workspace-data)
|
|
|
|
see-all-assets
|
|
(mf/use-fn
|
|
(fn []
|
|
(reset! summary?* false)))
|
|
|
|
libs-assets (mf/with-memo [file-data libraries summary?*]
|
|
(->> (vals libraries)
|
|
(map #(extract-assets file-data % summary?))
|
|
(filter (fn [[_ _ {:keys [components colors typographies]}]]
|
|
(or (seq components)
|
|
(seq colors)
|
|
(seq typographies))))))
|
|
|
|
update (mf/use-fn
|
|
(mf/deps file-id)
|
|
(fn [event]
|
|
(when-not updating?
|
|
(let [library-id (some-> (dom/get-target event)
|
|
(dom/get-data "library-id")
|
|
(uuid/parse))]
|
|
(st/emit!
|
|
(dwl/set-updating-library true)
|
|
(dwl/sync-file file-id library-id))))))]
|
|
|
|
[:div {:class (stl/css :updates-content)}
|
|
[:div {:class (stl/css :update-section)}
|
|
(if (empty? libs-assets)
|
|
[:div {:class (stl/css :section-list-empty)}
|
|
[:> empty-state* {:icon i/library
|
|
:text (tr "workspace.libraries.no-libraries-need-sync")}]]
|
|
[:*
|
|
[:div {:class (stl/css :section-title)} (tr "workspace.libraries.library-updates")]
|
|
|
|
[:div {:class (stl/css :section-list)}
|
|
(for [[{:keys [id name] :as library}
|
|
exceeded
|
|
{:keys [components colors typographies]}] libs-assets]
|
|
[:div {:class (stl/css :section-list-item)
|
|
:key (dm/str id)}
|
|
[:div {:class (stl/css :item-content)}
|
|
[:div {:class (stl/css :item-name)} name]
|
|
[:ul {:class (stl/css :item-contents)} (describe-library
|
|
(count components)
|
|
0
|
|
(count colors)
|
|
(count typographies))]]
|
|
[:button {:type "button"
|
|
:class (stl/css :item-update)
|
|
:disabled updating?
|
|
:data-library-id (dm/str id)
|
|
:on-click update}
|
|
(tr "workspace.libraries.update")]
|
|
|
|
[:div {:class (stl/css :libraries-updates)}
|
|
(when-not (empty? components)
|
|
[:div {:class (stl/css :libraries-updates-column)}
|
|
(for [component components]
|
|
[:div {:class (stl/css :libraries-updates-item)
|
|
:key (dm/str (:id component))}
|
|
(let [component (ctf/load-component-objects (:data library) component)
|
|
root-shape (ctf/get-component-root (:data library) component)]
|
|
[:*
|
|
[:& component-svg {:root-shape root-shape
|
|
:class (stl/css :component-svg)
|
|
:objects (:objects component)}]
|
|
[:div {:class (stl/css :name-block)}
|
|
[:span {:class (stl/css :item-name)
|
|
:title (:name component)}
|
|
(:name component)]]])])
|
|
(when (:components exceeded)
|
|
[:div {:class (stl/css :libraries-updates-item)
|
|
:key (uuid/next)}
|
|
[:div {:class (stl/css :name-block :ellipsis)}
|
|
[:span {:class (stl/css :item-name)} "(...)"]]])])
|
|
|
|
(when-not (empty? colors)
|
|
[:div {:class (stl/css :libraries-updates-column)
|
|
:style #js {"--bullet-size" "24px"}}
|
|
(for [color colors]
|
|
(let [default-name (cond
|
|
(:gradient color) (uc/gradient-type->string (get-in color [:gradient :type]))
|
|
(:color color) (:color color)
|
|
:else (:value color))]
|
|
[:div {:class (stl/css :libraries-updates-item)
|
|
:key (dm/str (:id color))}
|
|
[:*
|
|
[:& cb/color-bullet {:color {:color (:color color)
|
|
:id (:id color)
|
|
:opacity (:opacity color)}}]
|
|
[:div {:class (stl/css :name-block)}
|
|
[:span {:class (stl/css :item-name)
|
|
:title (:name color)}
|
|
(:name color)]
|
|
(when-not (= (:name color) default-name)
|
|
[:span.color-value (:color color)])]]]))
|
|
(when (:colors exceeded)
|
|
[:div {:class (stl/css :libraries-updates-item)
|
|
:key (uuid/next)}
|
|
[:div {:class (stl/css :name-block.ellipsis)}
|
|
[:span {:class (stl/css :item-name)} "(...)"]]])])
|
|
|
|
(when-not (empty? typographies)
|
|
[:div {:class (stl/css :libraries-updates-column)}
|
|
(for [typography typographies]
|
|
[:div {:class (stl/css :libraries-updates-item)
|
|
:key (dm/str (:id typography))}
|
|
[:*
|
|
[:div {:style {:font-family (:font-family typography)
|
|
:font-weight (:font-weight typography)
|
|
:font-style (:font-style typography)}}
|
|
(tr "workspace.assets.typography.sample")]
|
|
[:div {:class (stl/css :name-block)}
|
|
[:span {:class (stl/css :item-name)
|
|
:title (:name typography)}
|
|
(:name typography)]]]])
|
|
(when (:typographies exceeded)
|
|
[:div {:class (stl/css :libraries-updates-item)
|
|
:key (uuid/next)}
|
|
[:div {:class (stl/css :name-block.ellipsis)}
|
|
[:span {:class (stl/css :item-name)} "(...)"]]])])]
|
|
|
|
(when (or (pos? (:components exceeded))
|
|
(pos? (:colors exceeded))
|
|
(pos? (:typographies exceeded)))
|
|
[:& lb/link-button
|
|
{:on-click see-all-assets
|
|
:class (stl/css :libraries-updates-see-all)
|
|
:value (str "(" (tr "workspace.libraries.update.see-all-changes") ")")}])])]])]]))
|
|
|
|
(mf/defc libraries-dialog
|
|
{::mf/register modal/components
|
|
::mf/register-as :libraries-dialog}
|
|
[{:keys [starting-tab file-id]}]
|
|
(let [files (mf/deref refs/files)
|
|
file (get files file-id)
|
|
shared? (:is-shared file)
|
|
|
|
linked-libraries
|
|
(mf/with-memo [files file-id]
|
|
(refs/select-libraries files file-id))
|
|
|
|
linked-libraries
|
|
(mf/with-memo [linked-libraries file-id]
|
|
(d/removem (fn [[_ lib]]
|
|
(= (:id lib) file-id))
|
|
linked-libraries))
|
|
|
|
shared-libraries
|
|
(mf/deref refs/shared-files)
|
|
|
|
close-dialog-outside
|
|
(mf/use-fn
|
|
(fn [event]
|
|
(when (= (dom/get-target event) (dom/get-current-target event))
|
|
(modal/hide!))))
|
|
|
|
close-dialog
|
|
(mf/use-fn
|
|
(fn [_]
|
|
(modal/hide!)))
|
|
|
|
selected-tab*
|
|
(mf/use-state #(d/nilv starting-tab "libraries"))
|
|
|
|
selected-tab
|
|
(deref selected-tab*)
|
|
|
|
on-change-tab
|
|
(mf/use-fn #(reset! selected-tab* %))
|
|
|
|
tabs
|
|
(mf/with-memo []
|
|
[{:label (tr "workspace.libraries.libraries")
|
|
:id "libraries"}
|
|
{:label (tr "workspace.libraries.updates")
|
|
:id "updates"}])]
|
|
|
|
(mf/with-effect []
|
|
(st/emit! (dtm/fetch-shared-files)))
|
|
|
|
[:div {:class (stl/css :modal-overlay)
|
|
:on-click close-dialog-outside
|
|
:data-testid "libraries-modal"}
|
|
[:div {:class (stl/css :modal-dialog)}
|
|
[:button {:class (stl/css :close-btn)
|
|
:on-click close-dialog
|
|
:aria-label (tr "labels.close")
|
|
:data-testid "close-libraries"}
|
|
close-icon]
|
|
[:div {:class (stl/css :modal-title)}
|
|
(tr "workspace.libraries.libraries")]
|
|
|
|
[:> tab-switcher* {:tabs tabs
|
|
:selected selected-tab
|
|
:on-change on-change-tab}
|
|
(case selected-tab
|
|
"libraries"
|
|
[:> libraries-tab*
|
|
{:is-shared shared?
|
|
:linked-libraries linked-libraries
|
|
:shared-libraries shared-libraries}]
|
|
|
|
"updates"
|
|
[:> updates-tab*
|
|
{:file-id file-id
|
|
:libraries linked-libraries}])]]]))
|
|
|
|
(mf/defc v2-info-dialog
|
|
{::mf/register modal/components
|
|
::mf/register-as :v2-info}
|
|
[]
|
|
(let [handle-gotit-click
|
|
(mf/use-fn
|
|
(fn []
|
|
(modal/hide!)
|
|
(st/emit! (du/update-profile-props {:v2-info-shown true}))))]
|
|
|
|
[:div {:class (stl/css :modal-overlay)}
|
|
[:div {:class (stl/css :modal-dialog :modal-v2-info)}
|
|
[:div {:class (stl/css :modal-v2-title)}
|
|
"IMPORTANT INFORMATION ABOUT NEW COMPONENTS"]
|
|
[:div {:class (stl/css :modal-content)}
|
|
[:div {:class (stl/css :info-content)}
|
|
[:div {:class (stl/css :info-block)}
|
|
[:div {:class (stl/css :info-icon)} deprecated-icon/v2-icon-1]
|
|
[:div {:class (stl/css :info-block-title)}
|
|
"One physical source of truth"]
|
|
[:div {:class (stl/css :info-block-content)}
|
|
"Main components are now found at the design space. They act as a single source "
|
|
"of truth and can be worked on with their copies. This ensures consistency and "
|
|
"allows better control and synchronization."]]
|
|
|
|
[:div {:class (stl/css :info-block)}
|
|
[:div {:class (stl/css :info-icon)} deprecated-icon/v2-icon-2]
|
|
[:div {:class (stl/css :info-block-title)}
|
|
"Swap components"]
|
|
[:div {:class (stl/css :info-block-content)}
|
|
"Now, you can replace one component copy with another within your libraries. "
|
|
"The swap components functionality streamlines making changes, testing "
|
|
"variations, or updating elements without extensive manual adjustments."]]
|
|
|
|
[:div {:class (stl/css :info-block)}
|
|
[:div {:class (stl/css :info-icon)} deprecated-icon/v2-icon-3]
|
|
[:div {:class (stl/css :info-block-title)}
|
|
"Graphic assets no longer exist"]
|
|
[:div {:class (stl/css :info-block-content)}
|
|
"Graphic assets now disappear, so that all graphic assets become components. "
|
|
"This way, swapping between them is possible, and we avoid confusion about "
|
|
"what should go in each typology."]]
|
|
|
|
[:div {:class (stl/css :info-block)}
|
|
[:div {:class (stl/css :info-icon)} deprecated-icon/v2-icon-4]
|
|
[:div {:class (stl/css :info-block-title)}
|
|
"Main components page"]
|
|
[:div {:class (stl/css :info-block-content)}
|
|
"You might find that a new page called 'Main components' has appeared in "
|
|
"your file. On that page, you'll find all the main components that were "
|
|
"created in your files previously to this new version."]]]
|
|
|
|
[:div {:class (stl/css :info-bottom)}
|
|
[:button {:class (stl/css :primary-button)
|
|
:on-click handle-gotit-click} "I GOT IT"]]]]]))
|