Merge remote-tracking branch 'origin/staging' into staging-render

This commit is contained in:
Andrey Antukh 2025-12-10 10:09:58 +01:00
commit 45665a3c21
22 changed files with 268 additions and 174 deletions

View File

@ -21,6 +21,22 @@ jobs:
with: with:
gh_ref: ${{ github.ref_name }} gh_ref: ${{ github.ref_name }}
notify:
name: Notifications
runs-on: ubuntu-24.04
needs: build-docker
steps:
- name: Notify Mattermost
uses: mattermost/action-mattermost-notify@master
with:
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
MATTERMOST_CHANNEL: bot-alerts-cicd
TEXT: |
🐳 *[PENPOT] Docker image available.*
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
@infra
publish-final-tag: publish-final-tag:
if: ${{ !contains(github.ref_name, '-RC') && !contains(github.ref_name, '-alpha') && !contains(github.ref_name, '-beta') && contains(github.ref_name, '.') }} if: ${{ !contains(github.ref_name, '-RC') && !contains(github.ref_name, '-alpha') && !contains(github.ref_name, '-beta') && contains(github.ref_name, '.') }}
needs: build-docker needs: build-docker

View File

@ -92,6 +92,7 @@ example. It's still usable as before, we just removed the example.
- Fix U and E icon displayed in project list [Taiga #12806](https://tree.taiga.io/project/penpot/issue/12806) - Fix U and E icon displayed in project list [Taiga #12806](https://tree.taiga.io/project/penpot/issue/12806)
- Fix unpublish library modal not scrolling a long file list [Taiga #12285](https://tree.taiga.io/project/penpot/issue/12285) - Fix unpublish library modal not scrolling a long file list [Taiga #12285](https://tree.taiga.io/project/penpot/issue/12285)
- Fix incorrect interaction betwen hower and scroll on assets sidebar [Taiga #12389](https://tree.taiga.io/project/penpot/issue/12389) - Fix incorrect interaction betwen hower and scroll on assets sidebar [Taiga #12389](https://tree.taiga.io/project/penpot/issue/12389)
- Fix switch variants with paths [Taiga #12841](https://tree.taiga.io/project/penpot/issue/12841)
## 2.11.1 ## 2.11.1

View File

@ -12,8 +12,11 @@
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.files.variant :as cfv] [app.common.files.variant :as cfv]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.rect :as grc]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.geom.shapes.common :as gco]
[app.common.logging :as log] [app.common.logging :as log]
[app.common.logic.shapes :as cls] [app.common.logic.shapes :as cls]
[app.common.logic.variant-properties :as clvp] [app.common.logic.variant-properties :as clvp]
@ -26,6 +29,7 @@
[app.common.types.library :as ctl] [app.common.types.library :as ctl]
[app.common.types.page :as ctp] [app.common.types.page :as ctp]
[app.common.types.pages-list :as ctpl] [app.common.types.pages-list :as ctpl]
[app.common.types.path.segment :as segment]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi] [app.common.types.shape.interactions :as ctsi]
@ -1876,6 +1880,44 @@
roperations' roperations'
uoperations'))))))) uoperations')))))))
(defn- set-path-new-values
[current-shape prev-shape transform]
(let [new-content (segment/transform-content
(:content current-shape)
(gmt/transform-in (gpt/point 0 0) transform))
new-points (-> (segment/content->selrect new-content)
(grc/rect->points))
points-center (gco/points->center new-points)
new-selrect (gsh/calculate-selrect new-points points-center)
shape (assoc current-shape
:content new-content
:points new-points
:selrect new-selrect)
prev-center (segment/content-center (:content prev-shape))
delta (gpt/subtract points-center (first new-points))
new-pos (gpt/subtract prev-center delta)]
(gsh/absolute-move shape new-pos)))
(defn- switch-path-change-value
[prev-shape ;; The shape before the switch
current-shape ;; The shape after the switch (a clean copy)
ref-shape ;; The referenced shape on the main component
;; before the switch
attr]
(let [old-width (-> ref-shape :selrect :width)
new-width (-> prev-shape :selrect :width)
old-height (-> ref-shape :selrect :height)
new-height (-> prev-shape :selrect :height)
transform (-> (gpt/point (/ new-width old-width)
(/ new-height old-height))
(gmt/scale-matrix))
shape (set-path-new-values current-shape prev-shape transform)]
(get shape attr)))
(defn- switch-text-change-value (defn- switch-text-change-value
[prev-content ;; The :content of the text before the switch [prev-content ;; The :content of the text before the switch
@ -2027,6 +2069,10 @@
(= :content attr) (= :content attr)
(touched attr-group)) (touched attr-group))
path-change?
(and (= :path (:type current-shape))
(contains? #{:points :selrect :content} attr))
;; position-data is a special case because can be affected by :geometry-group and :content-group ;; position-data is a special case because can be affected by :geometry-group and :content-group
;; so, if the position-data changes but the geometry is touched we need to reset the position-data ;; so, if the position-data changes but the geometry is touched we need to reset the position-data
;; so it's calculated again ;; so it's calculated again
@ -2055,6 +2101,12 @@
(:content origin-ref-shape) (:content origin-ref-shape)
touched) touched)
path-change?
(switch-path-change-value previous-shape
current-shape
origin-ref-shape
attr)
:else :else
(get previous-shape attr))) (get previous-shape attr)))

View File

@ -281,7 +281,20 @@
(defn check-fn (defn check-fn
"Create a predefined check function" "Create a predefined check function"
[s & {:keys [hint type code]}] [s & {:keys [hint type code]}]
(let [s (schema s) (let [s #?(:clj
(schema s)
:cljs
(try
(schema s)
(catch :default cause
(let [data (ex-data cause)]
(if (= :malli.core/invalid-schema (:type data))
(throw (ex-info
(str "Invalid schema\n"
(pp/pprint-str (:data data)))
{}))
(throw cause))))))
validator* (delay (m/validator s)) validator* (delay (m/validator s))
explainer* (delay (m/explainer s)) explainer* (delay (m/explainer s))
hint (or ^boolean hint "check error") hint (or ^boolean hint "check error")

View File

@ -7,4 +7,5 @@ bb -i '(babashka.wait/wait-for-port "localhost" 9630)';
bb -i '(babashka.wait/wait-for-path "target/app.js")'; bb -i '(babashka.wait/wait-for-path "target/app.js")';
sleep 2; sleep 2;
export NODE_TLS_REJECT_UNAUTHORIZED=0
exec node target/app.js exec node target/app.js

View File

@ -100,7 +100,7 @@
(def browser-pool-factory (def browser-pool-factory
(letfn [(create [] (letfn [(create []
(p/let [opts #js {:args #js ["--font-render-hinting=none"]} (p/let [opts #js {:args #js ["--allow-insecure-localhost" "--font-render-hinting=none"]}
browser (.launch pw/chromium opts) browser (.launch pw/chromium opts)
id (swap! pool-browser-id inc)] id (swap! pool-browser-id inc)]
(l/info :origin "factory" :action "create" :browser-id id) (l/info :origin "factory" :action "create" :browser-id id)

View File

@ -74,7 +74,7 @@
(p/fmap (fn [resource] (p/fmap (fn [resource]
(assoc exchange :response/body resource))) (assoc exchange :response/body resource)))
(p/merr (fn [cause] (p/merr (fn [cause]
(l/error :hint "unexpected error on export multiple" (l/error :hint "unexpected error on single export"
:cause cause) :cause cause)
(p/rejected cause)))))) (p/rejected cause))))))
@ -94,7 +94,7 @@
(redis/pub! topic data)))) (redis/pub! topic data))))
on-error (fn [cause] on-error (fn [cause]
(l/error :hint "unexpected error on multiple exportation" :cause cause) (l/error :hint "unexpected error on multiple export" :cause cause)
(if wait (if wait
(p/rejected cause) (p/rejected cause)
(redis/pub! topic {:type :export-update (redis/pub! topic {:type :export-update

View File

@ -106,7 +106,7 @@
"@penpot/hljs": "portal:./vendor/hljs", "@penpot/hljs": "portal:./vendor/hljs",
"@penpot/mousetrap": "portal:./vendor/mousetrap", "@penpot/mousetrap": "portal:./vendor/mousetrap",
"@penpot/plugins-runtime": "1.3.2", "@penpot/plugins-runtime": "1.3.2",
"@penpot/svgo": "penpot/svgo#v3.1", "@penpot/svgo": "penpot/svgo#v3.2",
"@penpot/text-editor": "portal:./text-editor", "@penpot/text-editor": "portal:./text-editor",
"@tokens-studio/sd-transforms": "1.2.11", "@tokens-studio/sd-transforms": "1.2.11",
"@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch", "@zip.js/zip.js": "patch:@zip.js/zip.js@npm%3A2.7.60#~/.yarn/patches/@zip.js-zip.js-npm-2.7.60-b6b814410b.patch",

View File

@ -351,19 +351,31 @@
(on-success)))) (on-success))))
(rx/catch on-error)))))) (rx/catch on-error))))))
(def ^:private schema:create-invitation
[:and
[:map
[:emails {:optional true} [::sm/set ::sm/email]]
[:invitations {:optional true}
[:vector
[:map
[:email ::sm/email]
[:role [::sm/one-of ctt/valid-roles]]]]]
[:team-id ::sm/uuid]
[:resend? {:optional true} ::sm/boolean]]
[:fn (fn [attrs]
(or (contains? attrs :emails)
(contains? attrs :invitations)))]])
(def ^:private check-create-invitations-params
(sm/check-fn schema:create-invitation))
(defn create-invitations (defn create-invitations
"Unified function to create invitations. Supports two parameter formats: "Unified function to create invitations. Supports two parameter formats:
1. {:emails #{...} :role :admin :team-id uuid} - single role for all emails 1. {:emails #{...} :role :admin :team-id uuid} - single role for all emails
2. {:invitations [{:email ... :role ...}] :team-id uuid} - individual roles per email" 2. {:invitations [{:email ... :role ...}] :team-id uuid} - individual roles per email"
[{:keys [emails role team-id invitations resend?] :as params}] [{:keys [emails role team-id invitations resend?] :as params}]
(check-create-invitations-params params)
(assert (uuid? team-id))
;; Validate input format - must have either emails+role OR invitations
(assert (or (and emails role (sm/check-set-of-emails emails) (keyword? role))
(and invitations
(sm/check-set-of-emails (map :email invitations))
(every? #(contains? ctt/valid-roles (:role %)) invitations)))
"Must provide either emails+role or invitations with individual roles")
(ptk/reify ::create-invitations (ptk/reify ::create-invitations
ev/Event ev/Event

View File

@ -36,7 +36,7 @@
:text [:visibility :geometry :text :shadow :blur :stroke :layout-element] :text [:visibility :geometry :text :shadow :blur :stroke :layout-element]
:variant [:variant :geometry :fill :stroke :shadow :blur :layout :layout-element]}) :variant [:variant :geometry :fill :stroke :shadow :blur :layout :layout-element]})
(mf/defc attributes (mf/defc attributes*
[{:keys [page-id file-id shapes frame from libraries share-id objects color-space]}] [{:keys [page-id file-id shapes frame from libraries share-id objects color-space]}]
(let [shapes (hooks/use-equal-memo shapes) (let [shapes (hooks/use-equal-memo shapes)
first-shape (first shapes) first-shape (first shapes)

View File

@ -96,7 +96,7 @@
embed-images? (replace-map images-data))] embed-images? (replace-map images-data))]
(str/format page-template style-code markup-code))) (str/format page-template style-code markup-code)))
(mf/defc code (mf/defc code*
[{:keys [shapes frame on-expand from]}] [{:keys [shapes frame on-expand from]}]
(let [style-type* (mf/use-state "css") (let [style-type* (mf/use-state "css")
markup-type* (mf/use-state "html") markup-type* (mf/use-state "html")

View File

@ -16,8 +16,8 @@
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i] [app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]] [app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]]
[app.main.ui.icons :as deprecated-icon] [app.main.ui.icons :as deprecated-icon]
[app.main.ui.inspect.attributes :refer [attributes]] [app.main.ui.inspect.attributes :refer [attributes*]]
[app.main.ui.inspect.code :refer [code]] [app.main.ui.inspect.code :refer [code*]]
[app.main.ui.inspect.selection-feedback :refer [resolve-shapes]] [app.main.ui.inspect.selection-feedback :refer [resolve-shapes]]
[app.main.ui.inspect.styles :refer [styles-tab*]] [app.main.ui.inspect.styles :refer [styles-tab*]]
[app.util.dom :as dom] [app.util.dom :as dom]
@ -122,8 +122,7 @@
(fn [] (fn []
(if (seq shapes) (if (seq shapes)
(st/emit! (ptk/event ::ev/event {::ev/name "inspect-mode-click-element"})) (st/emit! (ptk/event ::ev/event {::ev/name "inspect-mode-click-element"}))
(handle-change-tab (if (contains? cf/flags :inspect-styles) :styles :info))) (handle-change-tab (if (contains? cf/flags :inspect-styles) :styles :info)))))
(reset! color-space* "hex")))
[:aside {:class (stl/css-case :settings-bar-right true [:aside {:class (stl/css-case :settings-bar-right true
:viewer-code (= from :viewer))} :viewer-code (= from :viewer))}
@ -189,41 +188,41 @@
:libraries libraries :libraries libraries
:file-id file-id}] :file-id file-id}]
:computed :computed
[:& attributes {:color-space color-space [:> attributes* {:color-space color-space
:page-id page-id :page-id page-id
:objects objects :objects objects
:file-id file-id :file-id file-id
:frame frame :frame frame
:shapes shapes :shapes shapes
:from from :from from
:libraries libraries :libraries libraries
:share-id share-id}] :share-id share-id}]
:code :code
[:& code {:frame frame [:> code* {:frame frame
:shapes shapes :shapes shapes
:on-expand handle-expand :on-expand handle-expand
:from from}])] :from from}])]
[:> tab-switcher* {:tabs tabs [:> tab-switcher* {:tabs tabs
:selected (name @section) :selected (name @section)
:on-change handle-change-tab :on-change handle-change-tab
:class (stl/css :viewer-tab-switcher)} :class (stl/css :viewer-tab-switcher)}
(case @section (case @section
:info :info
[:& attributes {:page-id page-id [:> attributes* {:page-id page-id
:objects objects :objects objects
:file-id file-id :file-id file-id
:frame frame :frame frame
:shapes shapes :shapes shapes
:from from :from from
:libraries libraries :libraries libraries
:share-id share-id}] :share-id share-id}]
:code :code
[:& code {:frame frame [:> code* {:frame frame
:shapes shapes :shapes shapes
:on-expand handle-expand :on-expand handle-expand
:from from}])])]] :from from}])])]]
[:div {:class (stl/css :empty)} [:div {:class (stl/css :empty)}
[:div {:class (stl/css :code-info)} [:div {:class (stl/css :code-info)}
[:span {:class (stl/css :placeholder-icon)} [:span {:class (stl/css :placeholder-icon)}

View File

@ -133,7 +133,7 @@
(swap! shorthands* assoc (:panel shorthand) (:property shorthand))))] (swap! shorthands* assoc (:panel shorthand) (:property shorthand))))]
[:ol {:class (stl/css :styles-tab) :aria-label (tr "labels.styles")} [:ol {:class (stl/css :styles-tab) :aria-label (tr "labels.styles")}
;; TOKENS PANEL ;; TOKENS PANEL
(when (or active-themes active-sets) (when (or (seq active-themes) (seq active-sets))
[:li [:li
[:> style-box* {:panel :token} [:> style-box* {:panel :token}
[:> tokens-panel* {:theme-paths active-themes :set-names active-sets}]]]) [:> tokens-panel* {:theme-paths active-themes :set-names active-sets}]]])

View File

@ -27,7 +27,7 @@
[app.main.ui.workspace.coordinates :as coordinates] [app.main.ui.workspace.coordinates :as coordinates]
[app.main.ui.workspace.libraries] [app.main.ui.workspace.libraries]
[app.main.ui.workspace.nudge] [app.main.ui.workspace.nudge]
[app.main.ui.workspace.palette :refer [palette]] [app.main.ui.workspace.palette :refer [palette*]]
[app.main.ui.workspace.plugins] [app.main.ui.workspace.plugins]
[app.main.ui.workspace.sidebar :refer [sidebar*]] [app.main.ui.workspace.sidebar :refer [sidebar*]]
[app.main.ui.workspace.sidebar.history :refer [history-toolbox*]] [app.main.ui.workspace.sidebar.history :refer [history-toolbox*]]
@ -84,8 +84,8 @@
node-ref (use-resize-observer on-resize)] node-ref (use-resize-observer on-resize)]
[:* [:*
(when (not ^boolean hide-ui?) (when (not ^boolean hide-ui?)
[:& palette {:layout layout [:> palette* {:layout layout
:on-change-palette-size on-resize-palette}]) :on-change-size on-resize-palette}])
[:section [:section
{:key (dm/str "workspace-" page-id) {:key (dm/str "workspace-" page-id)

View File

@ -156,7 +156,7 @@
(let [{:keys [modal title]} (get dwta/token-properties :color) (let [{:keys [modal title]} (get dwta/token-properties :color)
window-size (dom/get-window-size) window-size (dom/get-window-size)
left-sidebar (dom/get-element "left-sidebar-aside") left-sidebar (dom/get-element "left-sidebar-aside")
x-size (dom/get-data left-sidebar "left-sidebar-width") x-size (dom/get-data left-sidebar "width")
modal-height 392 modal-height 392
x (- (int x-size) 30) x (- (int x-size) 30)
y (- (/ (:height window-size) 2) (/ modal-height 2))] y (- (/ (:height window-size) 2) (/ modal-height 2))]

View File

@ -33,12 +33,13 @@
[okulary.core :as l] [okulary.core :as l]
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(def viewport (def ^:private ref:viewport
(l/derived :vport refs/workspace-local)) (l/derived :vport refs/workspace-local))
(defn calculate-palette-padding [rulers?] (defn- calculate-palette-style
[rulers?]
(let [left-sidebar (dom/get-element "left-sidebar-aside") (let [left-sidebar (dom/get-element "left-sidebar-aside")
left-sidebar-size (-> (dom/get-data left-sidebar "left-sidebar-width") left-sidebar-size (-> (dom/get-data left-sidebar "width")
(d/parse-integer)) (d/parse-integer))
rulers-width (if rulers? 22 0) rulers-width (if rulers? 22 0)
min-left-sidebar-width left-sidebar-default-width min-left-sidebar-width left-sidebar-default-width
@ -48,36 +49,46 @@
#js {"paddingLeft" (dm/str calculate-padding-left "px") #js {"paddingLeft" (dm/str calculate-padding-left "px")
"paddingRight" "322px"})) "paddingRight" "322px"}))
(mf/defc palette (mf/defc palette*
[{:keys [layout on-change-palette-size]}] [{:keys [layout on-change-size]}]
(let [color-palette? (:colorpalette layout) (let [color-palette? (:colorpalette layout)
text-palette? (:textpalette layout) text-palette? (:textpalette layout)
hide-palettes? (:hide-palettes layout) hide-palettes? (:hide-palettes layout)
workspace-read-only? (mf/use-ctx ctx/workspace-read-only?)
container (mf/use-ref nil)
state* (mf/use-state {:show-menu false})
state (deref state*)
show-menu? (:show-menu state)
selected (h/use-shared-state mdc/colorpalette-selected-broadcast-key :recent)
selected-text* (mf/use-state :file)
selected-text (deref selected-text*)
on-select (mf/use-fn #(reset! selected %))
rulers? (mf/deref refs/rulers?)
{:keys [on-pointer-down on-lost-pointer-capture on-pointer-move parent-ref size]}
(r/use-resize-hook :palette 72 54 80 :y true :bottom on-change-palette-size)
vport (mf/deref viewport) read-only? (mf/use-ctx ctx/workspace-read-only?)
vport-width (:width vport) container (mf/use-ref nil)
state* (mf/use-state #(-> {:show-menu false}))
state (deref state*)
show-menu? (:show-menu state)
selected (h/use-shared-state mdc/colorpalette-selected-broadcast-key :recent)
selected-text* (mf/use-state :file)
selected-text (deref selected-text*)
on-select (mf/use-fn #(reset! selected %))
rulers? (mf/deref refs/rulers?)
vport (mf/deref ref:viewport)
vport-width (get vport :width)
{:keys [on-pointer-down
on-lost-pointer-capture
on-pointer-move
parent-ref
size]}
(r/use-resize-hook :palette 72 54 80 :y true :bottom on-change-size)
on-resize on-resize
(mf/use-callback (mf/use-fn
(fn [_] (fn [_]
(let [dom (mf/ref-val container) (let [dom (mf/ref-val container)
width (obj/get dom "clientWidth")] width (obj/get dom "clientWidth")]
(swap! state* assoc :width width)))) (swap! state* assoc :width width))))
on-close-menu on-close-menu
(mf/use-callback (mf/use-fn
(fn [_] (fn [_]
(swap! state* assoc :show-menu false))) (swap! state* assoc :show-menu false)))
@ -100,7 +111,7 @@
(reset! selected-text* (:id lib))))) (reset! selected-text* (:id lib)))))
toggle-palettes toggle-palettes
(mf/use-callback (mf/use-fn
(fn [_] (fn [_]
(r/set-resize-type! :top) (r/set-resize-type! :top)
(dom/add-class! (dom/get-element-by-class "color-palette") "fade-out-down") (dom/add-class! (dom/get-element-by-class "color-palette") "fade-out-down")
@ -131,7 +142,9 @@
(vary-meta assoc ::ev/origin "workspace-left-toolbar")))) (vary-meta assoc ::ev/origin "workspace-left-toolbar"))))
(dom/blur! node)))) (dom/blur! node))))
any-palette? (or color-palette? text-palette?) any-palette?
(or color-palette? text-palette?)
size-classname size-classname
(cond (cond
(<= size 64) (stl/css :small-palette) (<= size 64) (stl/css :small-palette)
@ -142,16 +155,16 @@
(let [key1 (events/listen js/window "resize" on-resize)] (let [key1 (events/listen js/window "resize" on-resize)]
#(events/unlistenByKey key1))) #(events/unlistenByKey key1)))
(mf/use-layout-effect (mf/with-layout-effect []
#(let [dom (mf/ref-val parent-ref) (let [dom (mf/ref-val parent-ref)
width (obj/get dom "clientWidth")] width (obj/get dom "clientWidth")]
(swap! state* assoc :width width))) (swap! state* assoc :width width)))
[:div {:class (stl/css :palette-wrapper) [:div {:class (stl/css :palette-wrapper)
:id "palette-wrapper" :id "palette-wrapper"
:style (calculate-palette-padding rulers?) :style (calculate-palette-style rulers?)
:data-testid "palette"} :data-testid "palette"}
(when-not workspace-read-only? (when-not ^boolean read-only?
[:div {:ref parent-ref [:div {:ref parent-ref
:class (dm/str size-classname " " (stl/css-case :palettes true :class (dm/str size-classname " " (stl/css-case :palettes true
:wide any-palette? :wide any-palette?

View File

@ -27,7 +27,6 @@
[app.main.ui.workspace.left-header :refer [left-header*]] [app.main.ui.workspace.left-header :refer [left-header*]]
[app.main.ui.workspace.right-header :refer [right-header*]] [app.main.ui.workspace.right-header :refer [right-header*]]
[app.main.ui.workspace.sidebar.assets :refer [assets-toolbox*]] [app.main.ui.workspace.sidebar.assets :refer [assets-toolbox*]]
[app.main.ui.workspace.sidebar.collapsable-button :refer [collapsed-button*]]
[app.main.ui.workspace.sidebar.debug :refer [debug-panel*]] [app.main.ui.workspace.sidebar.debug :refer [debug-panel*]]
[app.main.ui.workspace.sidebar.debug-shape-info :refer [debug-shape-info*]] [app.main.ui.workspace.sidebar.debug-shape-info :refer [debug-shape-info*]]
[app.main.ui.workspace.sidebar.history :refer [history-toolbox*]] [app.main.ui.workspace.sidebar.history :refer [history-toolbox*]]
@ -44,19 +43,34 @@
;; --- Left Sidebar (Component) ;; --- Left Sidebar (Component)
(defn- on-collapse-left-sidebar (def ^:private toggle-collapse-left-sidebar
[] (partial st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))
(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))
(mf/defc collapse-button* (mf/defc collapse-button*
{::mf/private true}
[] []
;; NOTE: This custom button may be replace by an action button when this variant is designed ;; NOTE: This custom button may be replace by an action button when this variant is designed
[:button {:class (stl/css :collapse-sidebar-button) [:button {:class (stl/css :collapse-sidebar-button)
:on-click on-collapse-left-sidebar} :on-click toggle-collapse-left-sidebar}
[:> icon* {:icon-id i/arrow [:> icon* {:icon-id i/arrow
:size "s" :size "s"
:aria-label (tr "workspace.sidebar.collapse")}]]) :aria-label (tr "workspace.sidebar.collapse")}]])
(mf/defc collapsed-button*
{::mf/memo true
::mf/private true}
[]
[:div {:id "left-sidebar-aside"
:data-width "0"
:class (stl/css :collapsed-sidebar)}
[:div {:class (stl/css :collapsed-title)}
[:button {:class (stl/css :collapsed-button)
:title (tr "workspace.sidebar.expand")
:on-click toggle-collapse-left-sidebar}
[:> icon* {:icon-id i/arrow
:size "s"
:aria-label (tr "workspace.sidebar.expand")}]]]])
(mf/defc layers-content* (mf/defc layers-content*
{::mf/private true {::mf/private true
::mf/memo true} ::mf/memo true}
@ -97,6 +111,7 @@
[:> layers-toolbox* {:size-parent width}]])) [:> layers-toolbox* {:size-parent width}]]))
(mf/defc left-sidebar* (mf/defc left-sidebar*
{::mf/memo true} {::mf/memo true}
[{:keys [layout file page-id tokens-lib active-tokens resolved-active-tokens]}] [{:keys [layout file page-id tokens-lib active-tokens resolved-active-tokens]}]
@ -161,7 +176,7 @@
[:aside {:ref parent-ref [:aside {:ref parent-ref
:id "left-sidebar-aside" :id "left-sidebar-aside"
:data-testid "left-sidebar" :data-testid "left-sidebar"
:data-left-sidebar-width (str width) :data-width (str width)
:class aside-class :class aside-class
:style {:--left-sidebar-width (dm/str width "px")}} :style {:--left-sidebar-width (dm/str width "px")}}

View File

@ -116,6 +116,44 @@
} }
} }
.collapsed-sidebar {
@include deprecated.flexCenter;
position: absolute;
top: deprecated.$s-48;
left: 0;
padding: deprecated.$s-4;
border-radius: deprecated.$br-8;
background: var(--color-background-primary);
margin-inline-start: var(--sp-m);
}
.collapsed-title {
@include deprecated.flexCenter;
height: deprecated.$s-36;
width: deprecated.$s-24;
border-radius: deprecated.$br-8;
background: var(--color-background-secondary);
}
.collapsed-button {
@include deprecated.buttonStyle;
height: deprecated.$s-24;
width: deprecated.$s-16;
padding: 0;
border-radius: deprecated.$br-5;
svg {
@include deprecated.flexCenter;
height: deprecated.$s-16;
width: deprecated.$s-16;
color: transparent;
fill: none;
stroke: var(--icon-foreground);
}
&:hover {
svg {
stroke: var(--icon-foreground-hover);
}
}
}
.versions-tab { .versions-tab {
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;

View File

@ -1,29 +0,0 @@
;; 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.sidebar.collapsable-button
(:require-macros [app.main.style :as stl])
(:require
[app.main.data.workspace :as dw]
[app.main.store :as st]
[app.main.ui.ds.foundations.assets.icon :refer [icon*] :as i]
[app.util.i18n :refer [tr]]
[rumext.v2 :as mf]))
(mf/defc collapsed-button*
{::mf/memo true}
[]
(let [on-click (mf/use-fn #(st/emit! (dw/toggle-layout-flag :collapse-left-sidebar)))]
[:div {:id "left-sidebar-aside"
:data-size "0"
:class (stl/css :collapsed-sidebar)}
[:div {:class (stl/css :collapsed-title)}
[:button {:class (stl/css :collapsed-button)
:title (tr "workspace.sidebar.expand")
:on-click on-click}
[:> icon* {:icon-id i/arrow
:size "s"
:aria-label (tr "workspace.sidebar.expand")}]]]]))

View File

@ -1,45 +0,0 @@
// 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 "refactor/common-refactor.scss" as deprecated;
.collapsed-sidebar {
@include deprecated.flexCenter;
position: absolute;
top: deprecated.$s-48;
left: 0;
padding: deprecated.$s-4;
border-radius: deprecated.$br-8;
background: var(--color-background-primary);
margin-inline-start: var(--sp-m);
}
.collapsed-title {
@include deprecated.flexCenter;
height: deprecated.$s-36;
width: deprecated.$s-24;
border-radius: deprecated.$br-8;
background: var(--color-background-secondary);
}
.collapsed-button {
@include deprecated.buttonStyle;
height: deprecated.$s-24;
width: deprecated.$s-16;
padding: 0;
border-radius: deprecated.$br-5;
svg {
@include deprecated.flexCenter;
height: deprecated.$s-16;
width: deprecated.$s-16;
color: transparent;
fill: none;
stroke: var(--icon-foreground);
}
&:hover {
svg {
stroke: var(--icon-foreground-hover);
}
}
}

View File

@ -20,15 +20,21 @@
;; Component ------------------------------------------------------------------- ;; Component -------------------------------------------------------------------
(defn calculate-position (defn- calculate-position
"Calculates the style properties for the given coordinates and position" "Calculates the style properties for the given coordinates and position"
[{vh :height} position x y color?] [{vh :height} position x y token-type]
(let [;; picker height in pixels (let [; TODO: Revisit these harcoded values
;; TODO: Revisit these harcoded values modal-height (case token-type
h (if color? 610 510) :color
500
:typography
660
:shadow
660
400)
;; Checks for overflow outside the viewport height ;; Checks for overflow outside the viewport height
max-y (- vh h) max-y (- vh modal-height)
overflow-fix (max 0 (+ y (- 50) h (- vh))) overflow-fix (max 0 (+ y (- 50) modal-height (- vh)))
bottom-offset "1rem" bottom-offset "1rem"
top-offset (dm/str (- y 70) "px") top-offset (dm/str (- y 70) "px")
max-height-top (str "calc(100vh - " top-offset) max-height-top (str "calc(100vh - " top-offset)
@ -61,17 +67,19 @@
:top (dm/str (- y 70 overflow-fix) "px") :top (dm/str (- y 70 overflow-fix) "px")
:maxHeight max-height-top})))) :maxHeight max-height-top}))))
(defn use-viewport-position-style [x y position color?] (defn use-viewport-position-style [x y position token-type]
(let [vport (-> (l/derived :vport refs/workspace-local) (let [vport (-> (l/derived :vport refs/workspace-local)
(mf/deref))] (mf/deref))]
(-> (calculate-position vport position x y color?) (-> (calculate-position vport position x y token-type)
(clj->js)))) (clj->js))))
(mf/defc token-update-create-modal (mf/defc token-update-create-modal
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [x y position token token-type action selected-token-set-id] :as _args}] [{:keys [x y position token token-type action selected-token-set-id] :as _args}]
(let [wrapper-style (use-viewport-position-style x y position (= token-type :color)) (let [wrapper-style (use-viewport-position-style x y position token-type)
modal-size-large* (mf/use-state (= token-type :typography)) modal-size-large* (mf/use-state (or (= token-type :typography)
(= token-type :color)
(= token-type :shadow)))
modal-size-large? (deref modal-size-large*) modal-size-large? (deref modal-size-large*)
close-modal (mf/use-fn close-modal (mf/use-fn
(fn [] (fn []

View File

@ -1226,16 +1226,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@penpot/svgo@penpot/svgo#v3.1": "@penpot/svgo@penpot/svgo#v3.2":
version: 4.0.0 version: 4.0.0
resolution: "@penpot/svgo@https://github.com/penpot/svgo.git#commit=a46262c12c0d967708395972c374eb2adead4180" resolution: "@penpot/svgo@https://github.com/penpot/svgo.git#commit=8c9b0e32e9cb5f106085260bd9375f3c91a5010b"
dependencies: dependencies:
"@trysound/sax": "npm:0.2.0" "@trysound/sax": "npm:0.2.0"
css-select: "npm:^5.1.0" css-select: "npm:^5.1.0"
css-tree: "npm:^3.1.0" css-tree: "npm:^3.1.0"
csso: "npm:^5.0.5" csso: "npm:^5.0.5"
lodash: "npm:^4.17.21" lodash: "npm:^4.17.21"
checksum: 10c0/db5f81c99dec2765721d73b69bb30594869ebf657380dfb46709c79775b6c0dc1af678fe9fe51bbe2272a2c78d19c2694a12ec6578bcc41235fa4aff475c9416 checksum: 10c0/d7af2801451b97f8ffb17664147c609456f5bcc786c6d03b222546125260c0f268e750748311d61598e31f66610b00038d2b969635b1a15e5694647e19c6b63a
languageName: node languageName: node
linkType: hard linkType: hard
@ -4311,7 +4311,7 @@ __metadata:
"@penpot/hljs": "portal:./vendor/hljs" "@penpot/hljs": "portal:./vendor/hljs"
"@penpot/mousetrap": "portal:./vendor/mousetrap" "@penpot/mousetrap": "portal:./vendor/mousetrap"
"@penpot/plugins-runtime": "npm:1.3.2" "@penpot/plugins-runtime": "npm:1.3.2"
"@penpot/svgo": "penpot/svgo#v3.1" "@penpot/svgo": "penpot/svgo#v3.2"
"@penpot/text-editor": "portal:./text-editor" "@penpot/text-editor": "portal:./text-editor"
"@playwright/test": "npm:1.52.0" "@playwright/test": "npm:1.52.0"
"@storybook/addon-docs": "npm:10.0.4" "@storybook/addon-docs": "npm:10.0.4"