mirror of
https://github.com/penpot/penpot.git
synced 2026-08-29 08:08:46 +00:00
🎉 Make ui changes optional with config flag
This commit is contained in:
parent
7fc5057556
commit
e674705425
@ -3,6 +3,7 @@ import { WasmWorkspacePage } from "../pages/WasmWorkspacePage";
|
||||
|
||||
test.beforeEach(async ({ page }) => {
|
||||
await WasmWorkspacePage.init(page);
|
||||
await WasmWorkspacePage.mockConfigFlags(page, ["enable-token-lib-sync"]);
|
||||
});
|
||||
|
||||
test("User adds a library and its automatically selected in the color palette", async ({
|
||||
|
||||
@ -140,6 +140,8 @@
|
||||
token-sets-count (get summary :token-sets)
|
||||
token-themes-count (get summary :token-themes)
|
||||
|
||||
token-lib-sync? (contains? cf/flags :token-lib-sync)
|
||||
|
||||
elements-line
|
||||
(str/join " · "
|
||||
(cond-> []
|
||||
@ -167,18 +169,50 @@
|
||||
(pos? token-themes-count)
|
||||
(conj (tr "workspace.libraries.token-themes" (c token-themes-count)))))]
|
||||
|
||||
[:*
|
||||
(when-not (str/empty? elements-line)
|
||||
[:li {:class (stl/css :element-count-line)} elements-line])
|
||||
(if token-lib-sync?
|
||||
;; New format: show elements and tokens in separate lines
|
||||
[:*
|
||||
(when-not (str/empty? elements-line)
|
||||
[:li {:class (stl/css :element-count-line)} elements-line])
|
||||
|
||||
(when-not (str/empty? tokens-line)
|
||||
[:li {:class (stl/css :element-count-line)} tokens-line])
|
||||
(when-not (str/empty? tokens-line)
|
||||
[:li {:class (stl/css :element-count-line)} tokens-line])
|
||||
|
||||
(when hint-name
|
||||
[:li {:class (stl/css :hint-line)}
|
||||
"(" (tr "workspace.libraries.connected-through") " "
|
||||
[:span {:class (stl/css :hint-library-name)} hint-name]
|
||||
")"])]))
|
||||
(when hint-name
|
||||
[:li {:class (stl/css :hint-line)}
|
||||
"(" (tr "workspace.libraries.connected-through") " "
|
||||
[:span {:class (stl/css :hint-library-name)} hint-name]
|
||||
")"])]
|
||||
|
||||
;; Old format: show each item individually
|
||||
[:*
|
||||
(when (pos? components-count)
|
||||
[:li {:class (stl/css :element-count-legacy)}
|
||||
(tr "workspace.libraries.components" (c components-count))])
|
||||
|
||||
(when (pos? graphics-count)
|
||||
[:li {:class (stl/css :element-count-legacy)}
|
||||
(tr "workspace.libraries.graphics" (c graphics-count))])
|
||||
|
||||
(when (pos? colors-count)
|
||||
[:li {:class (stl/css :element-count-legacy)}
|
||||
(tr "workspace.libraries.colors" (c colors-count))])
|
||||
|
||||
(when (pos? typography-count)
|
||||
[:li {:class (stl/css :element-count-legacy)}
|
||||
(tr "workspace.libraries.typography" (c typography-count))])
|
||||
|
||||
(when (pos? tokens-count)
|
||||
[:li {:class (stl/css :element-count-legacy)}
|
||||
(tr "workspace.libraries.tokens" (c tokens-count))])
|
||||
|
||||
(when (pos? token-sets-count)
|
||||
[:li {:class (stl/css :element-count-legacy)}
|
||||
(tr "workspace.libraries.token-sets" (c token-sets-count))])
|
||||
|
||||
(when (pos? token-themes-count)
|
||||
[:li {:class (stl/css :element-count-legacy)}
|
||||
(tr "workspace.libraries.token-themes" (c token-themes-count))])])))
|
||||
|
||||
(mf/defc sample-library-entry*
|
||||
{::mf/private true}
|
||||
@ -231,6 +265,296 @@
|
||||
(when-let [tokens-lib (get data :tokens-lib)]
|
||||
(not (ctob/empty-lib? tokens-lib))))
|
||||
|
||||
(mf/defc libraries-tab-legacy*
|
||||
{::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 local-library)
|
||||
(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))
|
||||
;; When the unlinked library is the current tokens source,
|
||||
;; we must reset it to the local library.
|
||||
(when (cfo/effective-tokens-source? local-library library-id)
|
||||
(st/emit! (dwtl/set-tokens-source (:id local-library)))))))
|
||||
|
||||
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})))))
|
||||
|
||||
set-as-tokens-source
|
||||
(mf/use-fn
|
||||
(fn [event]
|
||||
(let [library-id (some-> (dom/get-current-target event)
|
||||
(dom/get-data "library-id")
|
||||
(uuid/parse))]
|
||||
(st/emit! (dwtl/set-tokens-source 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-legacy)}
|
||||
[: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-publish)}
|
||||
[:div {:class (stl/css :item-content)}
|
||||
[:div {:class (stl/css :item-title)} (tr "workspace.libraries.file-library")]
|
||||
[:ul {:class (stl/css :item-contents-legacy)}
|
||||
[:> library-description* {:summary summary}]]
|
||||
(when (contains? cf/flags :token-lib-sync)
|
||||
(if (cfo/effective-tokens-source? local-library (:id local-library))
|
||||
[:div (tr "workspace.libraries.tokens-source")]
|
||||
(when (not= (cfo/get-tokens-source local-library) (:id local-library))
|
||||
[:> button* {:variant "secondary"
|
||||
:type "button"
|
||||
:data-library-id (dm/str (:id local-library))
|
||||
:on-click set-as-tokens-source}
|
||||
(tr "workspace.libraries.set-as-tokens-source")])))]
|
||||
|
||||
(if ^boolean is-shared
|
||||
[:> button* {:variant "secondary"
|
||||
:type "button"
|
||||
:on-click unpublish}
|
||||
(tr "common.unpublish")]
|
||||
|
||||
[:> button* {:variant "primary"
|
||||
:type "button"
|
||||
:on-click publish}
|
||||
(tr "common.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 (stl/css :section-list-item-legacy)
|
||||
:key (dm/str id)
|
||||
:data-testid "library-item"}
|
||||
[:div {:class (stl/css :item-content)}
|
||||
[:div {:class (stl/css-case :item-name true
|
||||
:item-name-short has-tokens?)} name]
|
||||
[:ul {:class (stl/css :item-contents-legacy)}
|
||||
(let [summary (get-library-summary data)]
|
||||
[:*
|
||||
[:> library-description* {:summary summary}]
|
||||
(when (seq connected-to)
|
||||
[:div {:class (stl/css :connected-to-wrapper-legacy)}
|
||||
[:span "(" (tr "workspace.libraries.connected-to") " "]
|
||||
[:span {:class (stl/css :connected-to-values)} (str/join ", " connected-to-names)]
|
||||
[:span ")"]])])]
|
||||
(when (contains? cf/flags :token-lib-sync)
|
||||
(when (cfo/effective-tokens-source? local-library id)
|
||||
[:div (tr "workspace.libraries.tokens-source")]))]
|
||||
|
||||
[:div {:class (stl/css :library-actions)}
|
||||
(if (contains? cf/flags :token-lib-sync)
|
||||
(when (and (cfo/tokens-provider? (:data library))
|
||||
(not (cfo/effective-tokens-source? local-library id)))
|
||||
[:> button* {:variant "secondary"
|
||||
:type "button"
|
||||
:data-library-id (dm/str id)
|
||||
:on-click set-as-tokens-source}
|
||||
(tr "workspace.libraries.set-as-tokens-source")])
|
||||
(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-legacy)
|
||||
: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-legacy)}
|
||||
(let [summary (-> (:library-summary library)
|
||||
(adapt-backend-summary))]
|
||||
[:> library-description* {:summary summary}])]]
|
||||
|
||||
[:> icon-button* {:class (stl/css :item-button-shared)
|
||||
:variant "secondary"
|
||||
:data-library-id (dm/str id)
|
||||
:icon "add"
|
||||
:aria-label (tr "workspace.libraries.shared-library-btn")
|
||||
:on-click link-library}]])]
|
||||
|
||||
(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
|
||||
:key (dm/str (:id library))
|
||||
:importing importing*}])]]
|
||||
|
||||
:else
|
||||
(tr "workspace.libraries.no-matches-for" search-term))]))]]))
|
||||
|
||||
(mf/defc libraries-tab*
|
||||
{::mf/private true}
|
||||
[{:keys [linked-libraries shared-libraries]}]
|
||||
@ -814,8 +1138,10 @@
|
||||
(fn [_]
|
||||
(modal/hide!)))
|
||||
|
||||
token-lib-sync? (contains? cf/flags :token-lib-sync)
|
||||
|
||||
selected-tab*
|
||||
(mf/use-state #(d/nilv starting-tab "file"))
|
||||
(mf/use-state #(d/nilv starting-tab (if token-lib-sync? "file" "libraries")))
|
||||
|
||||
selected-tab
|
||||
(deref selected-tab*)
|
||||
@ -824,13 +1150,18 @@
|
||||
(mf/use-fn #(reset! selected-tab* %))
|
||||
|
||||
tabs
|
||||
(mf/with-memo []
|
||||
[{:label "This file"
|
||||
:id "file"}
|
||||
{:label (tr "workspace.libraries.libraries")
|
||||
:id "libraries"}
|
||||
{:label (tr "workspace.libraries.updates")
|
||||
:id "updates"}])]
|
||||
(mf/with-memo [token-lib-sync?]
|
||||
(if token-lib-sync?
|
||||
[{:label "This file"
|
||||
:id "file"}
|
||||
{:label (tr "workspace.libraries.libraries")
|
||||
:id "libraries"}
|
||||
{:label (tr "workspace.libraries.updates")
|
||||
:id "updates"}]
|
||||
[{:label (tr "workspace.libraries.libraries")
|
||||
:id "libraries"}
|
||||
{:label (tr "workspace.libraries.updates")
|
||||
:id "updates"}]))]
|
||||
|
||||
(mf/with-effect []
|
||||
(st/emit! (dtm/fetch-shared-files)))
|
||||
@ -852,14 +1183,20 @@
|
||||
:on-change on-change-tab}
|
||||
(case selected-tab
|
||||
"file"
|
||||
[:> file-tab* {:is-shared shared?
|
||||
:on-change-tab on-change-tab
|
||||
:linked-libraries linked-libraries
|
||||
:shared-libraries shared-libraries}]
|
||||
(when token-lib-sync?
|
||||
[:> file-tab* {:is-shared shared?
|
||||
:on-change-tab on-change-tab
|
||||
:linked-libraries linked-libraries
|
||||
:shared-libraries shared-libraries}])
|
||||
"libraries"
|
||||
[:> libraries-tab*
|
||||
{:linked-libraries linked-libraries
|
||||
:shared-libraries shared-libraries}]
|
||||
(if token-lib-sync?
|
||||
[:> libraries-tab*
|
||||
{:linked-libraries linked-libraries
|
||||
:shared-libraries shared-libraries}]
|
||||
[:> libraries-tab-legacy*
|
||||
{:is-shared shared?
|
||||
:linked-libraries linked-libraries
|
||||
:shared-libraries shared-libraries}])
|
||||
|
||||
"updates"
|
||||
[:> updates-tab*
|
||||
|
||||
@ -56,6 +56,7 @@
|
||||
padding-block-start: var(--sp-l);
|
||||
}
|
||||
|
||||
.libraries-content-legacy,
|
||||
.updates-content {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
@ -111,6 +112,10 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
.section-list-item-legacy {
|
||||
@extend %section-list-item-placeholder;
|
||||
}
|
||||
|
||||
.section-list-item {
|
||||
@extend %section-list-item-placeholder;
|
||||
|
||||
@ -283,6 +288,15 @@
|
||||
border-radius: $br-8;
|
||||
}
|
||||
|
||||
.item-contents-legacy {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
color: var(--library-content-foreground-color);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.item-contents {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
@ -297,6 +311,15 @@
|
||||
gap: var(--sp-xs);
|
||||
}
|
||||
|
||||
.element-count-legacy {
|
||||
white-space: nowrap;
|
||||
|
||||
&:not(:last-child)::after {
|
||||
content: "·";
|
||||
margin-inline: var(--sp-xs);
|
||||
}
|
||||
}
|
||||
|
||||
.element-count-line {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
@ -307,6 +330,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
.connected-to-wrapper-legacy {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.connected-to-values-legacy {
|
||||
color: var(--color-foreground-primary);
|
||||
}
|
||||
|
||||
.hint-line {
|
||||
color: var(--color-foreground-secondary);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user