Add per-group add button for typographies (#8895)

*  Add per-group add button for typographies

Add a "+" button to each typography group header, allowing users to
create new typographies directly inside a group instead of only at
the top level. The button only appears for local, editable files.

Closes #5275

* 📚 Add changelog entry for typography group add button

* 🐛 Fix typography group title button layout wrapping

* ♻️ Address review feedback for typography group add button

Signed-off-by: eureka928 <meobius123@gmail.com>
This commit is contained in:
Dream 2026-04-09 03:32:56 -04:00 committed by GitHub
parent e51e0c7933
commit 21217c5622
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 66 additions and 43 deletions

View File

@ -23,6 +23,7 @@
- Add drag-to-change for numeric inputs in workspace sidebar [Github #2466](https://github.com/penpot/penpot/issues/2466) - Add drag-to-change for numeric inputs in workspace sidebar [Github #2466](https://github.com/penpot/penpot/issues/2466)
- Add CSS linter [Taiga #13790](https://tree.taiga.io/project/penpot/us/13790) - Add CSS linter [Taiga #13790](https://tree.taiga.io/project/penpot/us/13790)
- Save and restore selection state in undo/redo (by @eureka928) [Github #6007](https://github.com/penpot/penpot/issues/6007) - Save and restore selection state in undo/redo (by @eureka928) [Github #6007](https://github.com/penpot/penpot/issues/6007)
- Add per-group add button for typographies (by @eureka928) [Github #5275](https://github.com/penpot/penpot/issues/5275)
### :bug: Bugs fixed ### :bug: Bugs fixed

View File

@ -859,50 +859,55 @@
"A higher level version of dwl/add-typography, and has mainly two "A higher level version of dwl/add-typography, and has mainly two
responsabilities: add the typography to the library and apply it to responsabilities: add the typography to the library and apply it to
the currently selected text shapes (being aware of the open text the currently selected text shapes (being aware of the open text
editors." editors.
[file-id] Optionally accepts a group-path to place the new typography inside
(ptk/reify ::add-typography a specific group."
ptk/WatchEvent ([file-id] (add-typography file-id nil))
(watch [_ state _] ([file-id group-path]
(let [selected (dsh/lookup-selected state) (ptk/reify ::add-typography
objects (dsh/lookup-page-objects state) ptk/WatchEvent
(watch [_ state _]
(let [selected (dsh/lookup-selected state)
objects (dsh/lookup-page-objects state)
xform (comp (keep (d/getf objects)) xform (comp (keep (d/getf objects))
(filter cfh/text-shape?)) (filter cfh/text-shape?))
shapes (into [] xform selected) shapes (into [] xform selected)
shape (first shapes) shape (first shapes)
values (current-text-values values (current-text-values
{:editor-state (dm/get-in state [:workspace-editor-state (:id shape)]) {:editor-state (dm/get-in state [:workspace-editor-state (:id shape)])
:shape shape :shape shape
:attrs txt/text-node-attrs}) :attrs txt/text-node-attrs})
multiple? (or (> 1 (count shapes)) multiple? (or (> 1 (count shapes))
(d/seek (partial = :multiple) (d/seek (partial = :multiple)
(vals values))) (vals values)))
values (-> (d/without-nils values) values (-> (d/without-nils values)
(select-keys (select-keys
(d/concat-vec txt/text-font-attrs (d/concat-vec txt/text-font-attrs
txt/text-spacing-attrs txt/text-spacing-attrs
txt/text-transform-attrs))) txt/text-transform-attrs)))
typ-id (uuid/next) typ-id (uuid/next)
typ (-> (if multiple? typ (-> (if multiple?
txt/default-typography txt/default-typography
(merge txt/default-typography values)) (merge txt/default-typography values))
(generate-typography-name) (generate-typography-name)
(assoc :id typ-id))] (assoc :id typ-id)
(cond-> (string? group-path)
(update :name #(str group-path " / " %))))]
(rx/concat (rx/concat
(rx/of (dwl/add-typography typ) (rx/of (dwl/add-typography typ)
(ptk/event ::ev/event {::ev/name "add-asset-to-library" (ptk/event ::ev/event {::ev/name "add-asset-to-library"
:asset-type "typography"})) :asset-type "typography"}))
(when (not multiple?) (when (not multiple?)
(rx/of (update-attrs (:id shape) (rx/of (update-attrs (:id shape)
{:typography-ref-id typ-id {:typography-ref-id typ-id
:typography-ref-file file-id})))))))) :typography-ref-file file-id})))))))))
;; -- New Editor ;; -- New Editor

View File

@ -23,7 +23,7 @@
[rumext.v2 :as mf])) [rumext.v2 :as mf]))
(mf/defc asset-group-title* (mf/defc asset-group-title*
[{:keys [file-id section path is-group-open on-rename on-ungroup on-group-combine-variants is-can-combine]}] [{:keys [file-id section path is-group-open on-rename on-ungroup on-group-combine-variants is-can-combine on-add]}]
(when-not (empty? path) (when-not (empty? path)
(let [[other-path last-path truncated] (cpn/compact-path path 35 true) (let [[other-path last-path truncated] (cpn/compact-path path 35 true)
menu-state (mf/use-state cmm/initial-context-menu-state) menu-state (mf/use-state cmm/initial-context-menu-state)
@ -76,6 +76,11 @@
:handler #(on-group-combine-variants path)}))}]] :handler #(on-group-combine-variants path)}))}]]
[:div {:class (stl/css :title-menu)} [:div {:class (stl/css :title-menu)}
(when on-add
[:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.assets.typography.add-typography")
:on-click on-add
:icon i/add}])
[:> icon-button* {:variant "ghost" [:> icon-button* {:variant "ghost"
:aria-label (tr "workspace.assets.component-group-options") :aria-label (tr "workspace.assets.component-group-options")
:on-click on-context-menu :on-click on-context-menu

View File

@ -25,6 +25,7 @@
} }
.title-menu { .title-menu {
display: flex;
visibility: hidden; visibility: hidden;
} }

View File

@ -133,7 +133,7 @@
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [file-id prefix groups open-groups force-open? file local? selected local-data [{:keys [file-id prefix groups open-groups force-open? file local? selected local-data
editing-id renaming-id on-asset-click handle-change on-rename-group editing-id renaming-id on-asset-click handle-change on-rename-group
on-ungroup on-context-menu selected-full]}] on-ungroup on-context-menu selected-full is-read-only]}]
(let [group-open? (if (false? (get open-groups prefix)) ;; if the user has closed it specifically, respect that (let [group-open? (if (false? (get open-groups prefix)) ;; if the user has closed it specifically, respect that
false false
(get open-groups prefix true)) (get open-groups prefix true))
@ -164,7 +164,14 @@
(mf/use-fn (mf/use-fn
(mf/deps dragging* prefix selected-paths selected-full move-typography) (mf/deps dragging* prefix selected-paths selected-full move-typography)
(fn [event] (fn [event]
(cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full move-typography)))] (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full move-typography)))
add-typography-to-group
(mf/use-fn
(mf/deps file-id prefix)
(fn [_]
(st/emit! (dw/set-assets-section-open file-id :typographies true)
(dwt/add-typography file-id prefix))))]
[:div {:class (stl/css :typographies-group) [:div {:class (stl/css :typographies-group)
:on-drag-enter on-drag-enter :on-drag-enter on-drag-enter
@ -176,7 +183,9 @@
:path prefix :path prefix
:is-group-open group-open? :is-group-open group-open?
:on-rename on-rename-group :on-rename on-rename-group
:on-ungroup on-ungroup}] :on-ungroup on-ungroup
:on-add (when (and local? (not is-read-only))
add-typography-to-group)}]
(when group-open? (when group-open?
[:* [:*
@ -229,7 +238,8 @@
:on-rename-group on-rename-group :on-rename-group on-rename-group
:on-ungroup on-ungroup :on-ungroup on-ungroup
:on-context-menu on-context-menu :on-context-menu on-context-menu
:selected-full selected-full}]))])])) :selected-full selected-full
:is-read-only is-read-only}]))])]))
(mf/defc typographies-section* (mf/defc typographies-section*
[{:keys [file file-id typographies open-status-ref selected [{:keys [file file-id typographies open-status-ref selected
@ -431,7 +441,8 @@
:on-rename-group on-rename-group :on-rename-group on-rename-group
:on-ungroup on-ungroup :on-ungroup on-ungroup
:on-context-menu on-context-menu :on-context-menu on-context-menu
:selected-full selected-full}] :selected-full selected-full
:is-read-only read-only?}]
(if is-local (if is-local
[:> cmm/assets-context-menu* [:> cmm/assets-context-menu*