mirror of
https://github.com/penpot/penpot.git
synced 2026-11-11 12:30:35 +00:00
* ✨ Auto link tokens when adding external libraries (provisional) * 🔧 Refactor tokens-lib initialization * 🔧 Add separated TokenStatus to store status apart of TokensLib * 🔧 Make all status operations use the new data structure * 🔧 Normalize status helper functions and access token sets by id * 🔧 Rename :tokens-file to :tokens-source * 🎉 Allow the user to choose the tokens-source of a file * 🎉 Make tokens library readonly when it's in an external file * 🎉 Show tokens in library summaries * 🎉 Show source info in sidebar * 🔧 Fix integration tests * 🐛 Propagate changes of token values in external library * 🎉 Layout updates * 🔧 Refactor tokens source calculations * 🔧 Add harder checks for nil or empty values in everything * 🐛 Fix some integration tests * 🔧 Add integration tests for tokens in external libs * 🔧 Validate and repair missing tokens status * 🎉 Make ui changes optional with config flag * 🐛 Propagate tokens after synchronizing components in ext library * 🐛 Propagate tokens after creating new instances * 🐛 Propagate tokens after synchronizing tokens in ext library * 🐛 Add a tokens source icon to libraries section (#11439) * 🐛 Add a tokens source icon to libraries section * 🐛 Fix ellipsis on library names * ♻️ Remove code under flag on legacy component * 🐛 Fix token theme name on inspect tab * 🎉 Add changes notification (#11476) * 🎉 Add changes notification * ♻️ Change fn names * 🐛 Fix tokens source label truncation and missing translations (#11533) * 🐛 Fix tokens source label truncation and missing translations The tokens source file name always showed, even for the current file, and long names wrapped onto a second line instead of truncating because the header used flex-wrap and overflow-wrap: break-word instead of single-line ellipsis. Show the source row unconditionally (it now displays "This file" when the source is the current file, matching the connected-library case), truncate the file name to one line with an ellipsis, and only attach a tooltip with the full name when the text is actually truncated. Replace the hardcoded UI strings with translated ones and add their English and Spanish entries. AI-assisted-by: claude-sonnet-5 * 🐛 Remove redundant effect dependency in tokens source file-name-truncated? was listed as a dependency of the with-effect that checks and observes label truncation, even though it isn't read inside the effect body. Since the effect itself flips that state via check-file-name-truncated, including it as a dependency caused the ResizeObserver to be needlessly disconnected and reconnected on every truncation change. AI-assisted-by: claude-sonnet-5 * 🐛 Fix small visual error * 🐛 Fix problem with plugins * 🐛 Fix playwright tests --------- Co-authored-by: Eva Marco <evamarcod@gmail.com> Co-authored-by: Eva Marco <eva.marco@kaleidos.net> Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
252 lines
7.5 KiB
Clojure
252 lines
7.5 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 SUBSIDIARY SL
|
|
|
|
(ns app.main.data.helpers
|
|
(:require
|
|
[app.common.data :as d]
|
|
[app.common.data.macros :as dm]
|
|
[app.common.files.helpers :as cfh]
|
|
[app.common.files.tokens :as cfo]
|
|
[app.common.geom.matrix :as gmt]
|
|
[app.common.geom.point :as gpt]
|
|
[app.common.geom.shapes :as gsh]
|
|
[app.common.types.path :as path]))
|
|
|
|
(defn lookup-profile
|
|
([state]
|
|
(:profile state))
|
|
([state profile-id]
|
|
(dm/get-in state [:profiles profile-id])))
|
|
|
|
(defn lookup-libraries
|
|
"Retrieve all libraries, including the local file."
|
|
[state]
|
|
(:files state))
|
|
|
|
(defn lookup-file
|
|
([state]
|
|
(lookup-file state (:current-file-id state)))
|
|
([state file-id]
|
|
(dm/get-in state [:files file-id])))
|
|
|
|
(defn lookup-file-data
|
|
([state]
|
|
(lookup-file-data state (:current-file-id state)))
|
|
([state file-id]
|
|
(dm/get-in state [:files file-id :data])))
|
|
|
|
;; TODOstatus perhaps this is not necessary, lookup-tokens-lib should be enough
|
|
(defn lookup-tokens-source-data
|
|
[state]
|
|
(let [current-file-data (lookup-file-data state)
|
|
tokens-source-id (cfo/get-effective-tokens-source current-file-data)]
|
|
(lookup-file-data state tokens-source-id)))
|
|
|
|
(defn lookup-tokens-lib
|
|
[state]
|
|
(let [tokens-source-data (lookup-tokens-source-data state)]
|
|
(cfo/get-tokens-lib tokens-source-data)))
|
|
|
|
(defn lookup-tokens-status
|
|
[state]
|
|
(let [current-file-data (lookup-file-data state)] ;; Tokens status is always in the current file
|
|
(cfo/get-tokens-status current-file-data)))
|
|
|
|
(defn get-page
|
|
[fdata page-id]
|
|
(dm/get-in fdata [:pages-index page-id]))
|
|
|
|
(defn lookup-page
|
|
([state]
|
|
(let [file-id (:current-file-id state)
|
|
page-id (:current-page-id state)]
|
|
(lookup-page state file-id page-id)))
|
|
([state page-id]
|
|
(let [file-id (:current-file-id state)]
|
|
(lookup-page state file-id page-id)))
|
|
([state file-id page-id]
|
|
(dm/get-in state [:files file-id :data :pages-index page-id])))
|
|
|
|
(defn lookup-page-objects
|
|
([state]
|
|
(lookup-page-objects state
|
|
(:current-file-id state)
|
|
(:current-page-id state)))
|
|
([state page-id]
|
|
(lookup-page-objects state
|
|
(:current-file-id state)
|
|
page-id))
|
|
([state file-id page-id]
|
|
(-> (lookup-page state file-id page-id)
|
|
(get :objects))))
|
|
|
|
(defn process-selected
|
|
([objects selected]
|
|
(process-selected objects selected nil))
|
|
|
|
([objects selected {:keys [omit-blocked?] :or {omit-blocked? false}}]
|
|
(let [selectable?
|
|
(fn [id]
|
|
(and (contains? objects id)
|
|
(or (not omit-blocked?)
|
|
(not (dm/get-in objects [id :blocked] false)))))
|
|
|
|
selected
|
|
(cfh/clean-loops objects selected)]
|
|
|
|
(into (d/ordered-set)
|
|
(filter selectable?)
|
|
selected))))
|
|
|
|
(defn split-text-shapes
|
|
"Split text shapes from non-text shapes"
|
|
[objects ids]
|
|
(loop [ids (seq ids)
|
|
text-ids []
|
|
shape-ids []]
|
|
(if-let [id (first ids)]
|
|
(let [shape (get objects id)]
|
|
(if (cfh/text-shape? shape)
|
|
(recur (rest ids)
|
|
(conj text-ids id)
|
|
shape-ids)
|
|
(recur (rest ids)
|
|
text-ids
|
|
(conj shape-ids id))))
|
|
[text-ids shape-ids])))
|
|
|
|
;; DEPRECATED
|
|
(defn lookup-selected-raw
|
|
[state]
|
|
(dm/get-in state [:workspace-local :selected]))
|
|
|
|
(defn get-selected-ids
|
|
[state]
|
|
(dm/get-in state [:workspace-local :selected]))
|
|
|
|
(defn lookup-selected
|
|
([state]
|
|
(lookup-selected state (:current-page-id state) nil))
|
|
([state options]
|
|
(lookup-selected state (:current-page-id state) options))
|
|
([state page-id options]
|
|
(let [objects (lookup-page-objects state page-id)
|
|
selected (dm/get-in state [:workspace-local :selected])]
|
|
(process-selected objects selected options))))
|
|
|
|
(defn lookup-shape
|
|
([state id]
|
|
(lookup-shape state (:current-page-id state) id))
|
|
|
|
([state page-id id]
|
|
(let [objects (lookup-page-objects state page-id)]
|
|
(get objects id))))
|
|
|
|
(defn lookup-shapes
|
|
([state ids]
|
|
(lookup-shapes state (:current-page-id state) ids))
|
|
([state page-id ids]
|
|
(let [objects (lookup-page-objects state page-id)]
|
|
(into [] (keep (d/getf objects)) ids))))
|
|
|
|
(defn update-file
|
|
([state f]
|
|
(update-file state (:current-file-id state) f))
|
|
([state file-id f]
|
|
(d/update-in-when state [:files file-id] f)))
|
|
|
|
(defn update-page
|
|
([state f]
|
|
(update-page state
|
|
(:current-file-id state)
|
|
(:current-page-id state)
|
|
f))
|
|
([state page-id f]
|
|
(update-page state
|
|
(:current-file-id state)
|
|
page-id
|
|
f))
|
|
([state file-id page-id f]
|
|
(d/update-in-when state [:files file-id :data :pages-index page-id] f)))
|
|
|
|
(defn filter-shapes
|
|
([state filter-fn]
|
|
(filter-shapes state (:current-page-id state) filter-fn))
|
|
([state page-id filter-fn]
|
|
(let [objects (lookup-page-objects state page-id)]
|
|
(into [] (filter filter-fn) (vals objects)))))
|
|
|
|
(defn select-bool-children
|
|
[state parent-id]
|
|
(let [objects (lookup-page-objects state)
|
|
|
|
shape-modifiers
|
|
(:workspace-modifiers state)
|
|
|
|
content-modifiers
|
|
(dm/get-in state [:workspace-local :edit-path])]
|
|
|
|
(reduce (fn [result id]
|
|
(if-let [shape (get objects id)]
|
|
(let [modifiers (dm/get-in shape-modifiers [id :modifiers])
|
|
shape (if (some? modifiers)
|
|
(gsh/transform-shape shape modifiers)
|
|
shape)
|
|
modifiers (dm/get-in content-modifiers [id :content-modifiers])
|
|
shape (if (some? modifiers)
|
|
(update shape :content path/apply-content-modifiers modifiers)
|
|
shape)]
|
|
(assoc result id shape))
|
|
result))
|
|
{}
|
|
(cfh/get-children-ids objects parent-id))))
|
|
|
|
(defn get-viewport-center
|
|
[state]
|
|
(when-let [{:keys [x y width height]} (get-in state [:workspace-local :vbox])]
|
|
(gpt/point (+ x (/ width 2)) (+ y (/ height 2)))))
|
|
|
|
(defn lookup-team-files
|
|
([state]
|
|
(lookup-team-files state (:current-team-id state)))
|
|
([state team-id]
|
|
(->> state
|
|
:files
|
|
(filter #(= team-id (:team-id (val %))))
|
|
(into {}))))
|
|
|
|
(defn lookup-team-projects
|
|
([state]
|
|
(lookup-team-projects (:current-team-id state)))
|
|
([state team-id]
|
|
(->> state
|
|
:projects
|
|
(filter #(= team-id (:team-id (val %))))
|
|
(into {}))))
|
|
|
|
(defn lookup-team
|
|
"The team identified by `team-id`, looked up first in the membership
|
|
`:teams` map and falling back to the directly-opened `:current-team`.
|
|
The fallback covers organization-owner access to teams the profile is not a
|
|
member of, which are kept out of `:teams` so they don't leak into the
|
|
teams listing."
|
|
([state]
|
|
(lookup-team state (:current-team-id state)))
|
|
([state team-id]
|
|
(or (dm/get-in state [:teams team-id])
|
|
(let [current (:current-team state)]
|
|
(when (= team-id (:id current))
|
|
current)))))
|
|
|
|
(defn get-selrect
|
|
[selrect-transform shape]
|
|
(if (some? selrect-transform)
|
|
(let [{:keys [center width height transform]} selrect-transform]
|
|
[(gsh/center->rect center width height)
|
|
(gmt/transform-in center transform)])
|
|
[(dm/get-prop shape :selrect)
|
|
(gsh/transform-matrix shape)]))
|