mirror of
https://github.com/penpot/penpot.git
synced 2026-07-24 15:08:24 +00:00
🎉 Add page multi-selection in the workspace sitemap (#10581)
* 🎉 Add page multi-selection in the workspace sitemap * ♻️ Simplify page selection state updates with single assoc * 🐛 Fix SCSS issue * ♻️ Update some components to new syntax * ♻️ Adapt SCSS to the new guidelines * ♻️ SCSS cleanup --------- Co-authored-by: Luis de Dios <luis.dedios@kaleidos.net>
This commit is contained in:
parent
fa996ab240
commit
5dff551f31
@ -1207,9 +1207,20 @@
|
||||
(dm/assert! (gpt/point? position))
|
||||
(ptk/reify ::show-page-item-context-menu
|
||||
ptk/WatchEvent
|
||||
(watch [_ _ _]
|
||||
(rx/of (show-context-menu
|
||||
(-> params (assoc :kind :page :selected (:id page))))))))
|
||||
(watch [_ state _]
|
||||
(let [id (:id page)
|
||||
selected (dm/get-in state [:workspace-local :selected-pages])
|
||||
;; When the right-clicked page is part of a multi-selection we
|
||||
;; keep it; otherwise the menu targets just that page.
|
||||
multi? (and (contains? selected id) (> (count selected) 1))]
|
||||
(rx/concat
|
||||
(if multi?
|
||||
(rx/empty)
|
||||
(rx/of (dwpg/select-page id)))
|
||||
(rx/of (show-context-menu
|
||||
(-> params (assoc :kind :page
|
||||
:selected id
|
||||
:selected-pages (if multi? selected #{id}))))))))))
|
||||
|
||||
(defn show-track-context-menu
|
||||
[{:keys [grid-id type index] :as params}]
|
||||
@ -1651,6 +1662,11 @@
|
||||
(dm/export dwpg/duplicate-page)
|
||||
(dm/export dwpg/rename-page)
|
||||
(dm/export dwpg/delete-page)
|
||||
(dm/export dwpg/delete-pages)
|
||||
(dm/export dwpg/select-page)
|
||||
(dm/export dwpg/toggle-page-selection)
|
||||
(dm/export dwpg/select-pages-range)
|
||||
(dm/export dwpg/clear-page-selection)
|
||||
|
||||
;; Shapes
|
||||
(dm/export dwsh/delete-shapes)
|
||||
|
||||
@ -135,7 +135,8 @@
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [local (-> (:workspace-local state)
|
||||
(dissoc :edition :edit-path :selected))
|
||||
(dissoc :edition :edit-path :selected
|
||||
:selected-pages :selected-pages-anchor))
|
||||
exit? (not= :workspace (rt/lookup-name state))
|
||||
state (-> state
|
||||
(update :workspace-cache assoc [file-id page-id] local)
|
||||
@ -395,3 +396,104 @@
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(when (= id (:current-page-id state))
|
||||
(dcm/go-to-workspace {:page-id (first pages)})))))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Page selection (sitemap multi-selection)
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(defn clear-page-selection
|
||||
"Clear the sitemap multi-selection state."
|
||||
[]
|
||||
(ptk/reify ::clear-page-selection
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-local dissoc :selected-pages :selected-pages-anchor))))
|
||||
|
||||
(defn select-page
|
||||
"Set the sitemap selection to a single page (plain click). It also
|
||||
sets the anchor used by range (shift+click) selection."
|
||||
[id]
|
||||
(ptk/reify ::select-page
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-local assoc
|
||||
:selected-pages (d/ordered-set id)
|
||||
:selected-pages-anchor id))))
|
||||
|
||||
(defn toggle-page-selection
|
||||
"Add or remove a page from the sitemap multi-selection (ctrl/cmd + click)."
|
||||
[id]
|
||||
(ptk/reify ::toggle-page-selection
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [current-id (:current-page-id state)
|
||||
selected (or (not-empty (dm/get-in state [:workspace-local :selected-pages]))
|
||||
(d/ordered-set current-id))
|
||||
selected (if (contains? selected id)
|
||||
(disj selected id)
|
||||
(conj selected id))]
|
||||
(update state :workspace-local assoc
|
||||
:selected-pages selected
|
||||
:selected-pages-anchor id)))))
|
||||
|
||||
(defn select-pages-range
|
||||
"Select every page between the current anchor and `id`, both included
|
||||
(shift + click). The anchor is kept unchanged."
|
||||
[id]
|
||||
(ptk/reify ::select-pages-range
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [current-id (:current-page-id state)
|
||||
pages (-> (dsh/lookup-file-data state) :pages vec)
|
||||
anchor (or (dm/get-in state [:workspace-local :selected-pages-anchor])
|
||||
current-id)
|
||||
a-idx (d/index-of pages anchor)
|
||||
b-idx (d/index-of pages id)]
|
||||
(if (and (some? a-idx) (some? b-idx))
|
||||
(let [start (min a-idx b-idx)
|
||||
end (inc (max a-idx b-idx))
|
||||
range (subvec pages start end)]
|
||||
(update state :workspace-local assoc
|
||||
:selected-pages (into (d/ordered-set) range)))
|
||||
state)))))
|
||||
|
||||
(defn delete-pages
|
||||
"Delete a collection of pages in a single change (single undo entry),
|
||||
always keeping at least one page in the file."
|
||||
[ids]
|
||||
(ptk/reify ::delete-pages
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
(let [file-id (:current-file-id state)
|
||||
fdata (dsh/lookup-file-data state file-id)
|
||||
pindex (:pages-index fdata)
|
||||
pages (:pages fdata)
|
||||
|
||||
ids (set ids)
|
||||
;; A file must keep at least one page: if every page is
|
||||
;; selected, keep the first one in page order.
|
||||
ids (if (>= (count ids) (count pages))
|
||||
(set (rest (filter ids pages)))
|
||||
ids)
|
||||
|
||||
;; Pages to delete, in page order.
|
||||
del-ids (filter ids pages)
|
||||
remaining (remove ids pages)
|
||||
|
||||
changes (reduce
|
||||
(fn [changes id]
|
||||
(let [page (-> (get pindex id)
|
||||
(assoc :index (d/index-of pages id)))]
|
||||
(-> changes
|
||||
(delete-page-components page)
|
||||
(pcb/del-page page))))
|
||||
(-> (pcb/empty-changes it)
|
||||
(pcb/with-library-data fdata))
|
||||
del-ids)]
|
||||
|
||||
(if (empty? del-ids)
|
||||
(rx/empty)
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(clear-page-selection)
|
||||
(when (contains? ids (:current-page-id state))
|
||||
(dcm/go-to-workspace {:page-id (first remaining)}))))))))
|
||||
|
||||
@ -253,6 +253,10 @@
|
||||
(def editing-page-item
|
||||
(l/derived :page-item workspace-local))
|
||||
|
||||
;; set of pages selected in the sitemap (multi-selection)
|
||||
(def selected-pages
|
||||
(l/derived :selected-pages workspace-local))
|
||||
|
||||
(def current-hover-ids
|
||||
(l/derived :hover-ids context-menu))
|
||||
|
||||
|
||||
@ -705,6 +705,8 @@
|
||||
[{:keys [mdata]}]
|
||||
(let [page (:page mdata)
|
||||
deletable? (:deletable? mdata)
|
||||
selected-pages (:selected-pages mdata)
|
||||
multi? (> (count selected-pages) 1)
|
||||
id (:id page)
|
||||
delete-fn #(st/emit! (dw/delete-page id))
|
||||
do-delete #(st/emit! (modal/show
|
||||
@ -712,20 +714,31 @@
|
||||
:title (tr "modals.delete-page.title")
|
||||
:message (tr "modals.delete-page.body")
|
||||
:on-accept delete-fn}))
|
||||
delete-many-fn #(st/emit! (dw/delete-pages selected-pages))
|
||||
do-delete-many #(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-pages.title")
|
||||
:message (tr "modals.delete-pages.body")
|
||||
:on-accept delete-many-fn}))
|
||||
do-duplicate #(st/emit!
|
||||
(dw/duplicate-page id)
|
||||
(ev/event {::ev/name "duplicate-page"}))
|
||||
do-rename #(st/emit! (dw/start-rename-page-item id))]
|
||||
|
||||
[:*
|
||||
(when deletable?
|
||||
[:> menu-entry* {:title (tr "workspace.assets.delete")
|
||||
:on-click do-delete}])
|
||||
(if multi?
|
||||
;; When several pages are selected, the only available action is
|
||||
;; deleting all of them at once.
|
||||
[:> menu-entry* {:title (tr "workspace.assets.delete-pages")
|
||||
:on-click do-delete-many}]
|
||||
[:*
|
||||
(when deletable?
|
||||
[:> menu-entry* {:title (tr "workspace.assets.delete")
|
||||
:on-click do-delete}])
|
||||
|
||||
[:> menu-entry* {:title (tr "workspace.assets.rename")
|
||||
:on-click do-rename}]
|
||||
[:> menu-entry* {:title (tr "workspace.assets.duplicate")
|
||||
:on-click do-duplicate}]]))
|
||||
[:> menu-entry* {:title (tr "workspace.assets.rename")
|
||||
:on-click do-rename}]
|
||||
[:> menu-entry* {:title (tr "workspace.assets.duplicate")
|
||||
:on-click do-duplicate}]])))
|
||||
|
||||
(mf/defc viewport-context-menu*
|
||||
[]
|
||||
|
||||
@ -62,9 +62,10 @@
|
||||
|
||||
;; --- Page Item
|
||||
|
||||
(mf/defc page-item
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [page index deletable? selected? editing? hovering? current-page-id]}]
|
||||
(mf/defc page-item*
|
||||
{::mf/private true
|
||||
::mf/wrap-props false}
|
||||
[{:keys [page index is-deletable is-selected is-editing is-hovering current-page-id]}]
|
||||
(let [input-ref (mf/use-ref)
|
||||
id (:id page)
|
||||
name (:name page "")
|
||||
@ -76,38 +77,53 @@
|
||||
on-click
|
||||
(mf/use-fn
|
||||
(mf/deps id current-page-id is-separator?)
|
||||
(fn []
|
||||
(fn [event]
|
||||
(when-not is-separator?
|
||||
;; WASM page transitions:
|
||||
;; - Capture the current page (A) once
|
||||
;; - Show a blurred snapshot while the target page (B/C/...) renders
|
||||
;; - If the user clicks again during the transition, keep showing the original (A) snapshot
|
||||
(if (and (features/active-feature? @st/state "render-wasm/v1")
|
||||
(not= id current-page-id))
|
||||
(-> (if @wasm.api/page-transition?
|
||||
(p/resolved nil)
|
||||
;; Blur with Skia, then capture the already-blurred frame.
|
||||
(do (wasm.api/render-blurred-snapshot!)
|
||||
(wasm.api/capture-canvas-snapshot)))
|
||||
(p/finally
|
||||
(fn []
|
||||
(wasm.api/apply-canvas-blur)
|
||||
;; Two RAF so the overlay paints before navigation.
|
||||
(timers/raf
|
||||
(fn []
|
||||
(timers/raf navigate-fn))))))
|
||||
(navigate-fn)))))
|
||||
(cond
|
||||
;; Shift + click: select the range of pages from the anchor
|
||||
;; to this page (does not navigate).
|
||||
(kbd/shift? event)
|
||||
(st/emit! (dw/select-pages-range id))
|
||||
|
||||
;; Ctrl/Cmd + click: add/remove this page to/from the
|
||||
;; multi-selection (does not navigate).
|
||||
(kbd/mod? event)
|
||||
(st/emit! (dw/toggle-page-selection id))
|
||||
|
||||
;; Plain click: reset the selection to this page and
|
||||
;; navigate to it.
|
||||
:else
|
||||
(do
|
||||
(st/emit! (dw/select-page id))
|
||||
;; WASM page transitions:
|
||||
;; - Capture the current page (A) once
|
||||
;; - Show a blurred snapshot while the target page (B/C/...) renders
|
||||
;; - If the user clicks again during the transition, keep showing the original (A) snapshot
|
||||
(if (and (features/active-feature? @st/state "render-wasm/v1")
|
||||
(not= id current-page-id))
|
||||
(-> (if @wasm.api/page-transition?
|
||||
(p/resolved nil)
|
||||
;; Blur with Skia, then capture the already-blurred frame.
|
||||
(do (wasm.api/render-blurred-snapshot!)
|
||||
(wasm.api/capture-canvas-snapshot)))
|
||||
(p/finally
|
||||
(fn []
|
||||
(wasm.api/apply-canvas-blur)
|
||||
;; Two RAF so the overlay paints before navigation.
|
||||
(timers/raf
|
||||
(fn []
|
||||
(timers/raf navigate-fn))))))
|
||||
(navigate-fn)))))))
|
||||
|
||||
on-delete
|
||||
(mf/use-fn
|
||||
(mf/deps id)
|
||||
(fn [event]
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (modal/show
|
||||
{:type :confirm
|
||||
:title (tr "modals.delete-page.title")
|
||||
:message (tr "modals.delete-page.body")
|
||||
:on-accept delete-fn}))))
|
||||
(st/emit! (modal/show {:type :confirm
|
||||
:title (tr "modals.delete-page.title")
|
||||
:message (tr "modals.delete-page.body")
|
||||
:on-accept delete-fn}))))
|
||||
|
||||
on-double-click
|
||||
(mf/use-fn
|
||||
@ -153,7 +169,7 @@
|
||||
:data {:id id
|
||||
:index index
|
||||
:name (:name page)}
|
||||
:draggable? (and (not read-only?) (not editing?)))
|
||||
:draggable? (and (not read-only?) (not is-editing)))
|
||||
|
||||
on-context-menu
|
||||
(mf/use-fn
|
||||
@ -166,50 +182,49 @@
|
||||
(st/emit! (dw/show-page-item-context-menu
|
||||
{:position position
|
||||
:page page
|
||||
:deletable? deletable?}))))))]
|
||||
:deletable? is-deletable}))))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps selected?)
|
||||
(mf/deps is-selected)
|
||||
(fn []
|
||||
(when selected?
|
||||
(when is-selected
|
||||
(let [node (mf/ref-val dref)]
|
||||
(dom/scroll-into-view-if-needed! node)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps editing?)
|
||||
(mf/deps is-editing)
|
||||
(fn []
|
||||
(when editing?
|
||||
(when is-editing
|
||||
(let [edit-input (mf/ref-val input-ref)]
|
||||
(dom/select-text! edit-input))
|
||||
nil)))
|
||||
|
||||
(let [selected? (and selected? (not is-separator?))]
|
||||
[:li {:class (stl/css-case
|
||||
:page-element true
|
||||
:separator is-separator?
|
||||
:selected selected?
|
||||
:dnd-over-top (= (:over dprops) :top)
|
||||
:dnd-over-bot (= (:over dprops) :bot))
|
||||
(let [selected? (and is-selected (not is-separator?))]
|
||||
[:li {:class (stl/css-case :page-item true
|
||||
:separator is-separator?
|
||||
:selected selected?
|
||||
:dnd-over-top (= (:over dprops) :top)
|
||||
:dnd-over-bot (= (:over dprops) :bot))
|
||||
:ref dref}
|
||||
[:div {:class (stl/css-case
|
||||
:element-list-body true
|
||||
:separator-body is-separator?
|
||||
:hover (and hovering? (not is-separator?))
|
||||
:selected selected?)
|
||||
[:div {:class (stl/css-case :page-item-body true
|
||||
:separator is-separator?
|
||||
:hover (and is-hovering (not is-separator?))
|
||||
:selected selected?)
|
||||
:data-testid (dm/str "page-" id)
|
||||
:tab-index "0"
|
||||
:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
:on-context-menu on-context-menu}
|
||||
(if (and is-separator? (not editing?))
|
||||
[:div {:class (stl/css :page-separator)
|
||||
(if (and is-separator? (not is-editing))
|
||||
[:div {:class (stl/css :page-divider)
|
||||
:data-testid "page-separator"}]
|
||||
[:*
|
||||
(when-not is-separator?
|
||||
[:div {:class (stl/css :page-icon)}
|
||||
[:> icon* {:icon-id i/document :size "s"}]])
|
||||
(if editing?
|
||||
[:input {:class (stl/css :element-name)
|
||||
[:div {:class (stl/css :page-item-icon)}
|
||||
[:> icon* {:icon-id i/document
|
||||
:size "s"}]])
|
||||
(if is-editing
|
||||
[:input {:class (stl/css :page-item-input)
|
||||
:type "text"
|
||||
:ref input-ref
|
||||
:on-blur on-blur
|
||||
@ -217,32 +232,35 @@
|
||||
:auto-focus true
|
||||
:default-value name}]
|
||||
[:*
|
||||
[:span {:class (stl/css :page-name) :title name :data-testid "page-name"}
|
||||
[:span {:class (stl/css :page-item-label)
|
||||
:title name
|
||||
:data-testid "page-name"}
|
||||
name]
|
||||
[:div {:class (stl/css :page-actions)}
|
||||
(when (and deletable? (not read-only?))
|
||||
[:div {:class (stl/css :page-item-actions)}
|
||||
(when (and is-deletable (not read-only?))
|
||||
[:> icon-button* {:variant "action"
|
||||
:aria-label (tr "modals.delete-page.title")
|
||||
:on-click on-delete
|
||||
:icon-size "s"
|
||||
:class (stl/css :page-delete-button)
|
||||
:icon-class (stl/css :page-delete-button-icon)
|
||||
:class (stl/css :page-delete-btn)
|
||||
:icon-class (stl/css :page-delete-icon)
|
||||
:icon i/delete}])]])])]])))
|
||||
|
||||
;; --- Page Item Wrapper
|
||||
|
||||
(mf/defc page-item-wrapper
|
||||
{::mf/wrap-props false}
|
||||
[{:keys [page-id index deletable? selected? editing? current-page-id]}]
|
||||
(mf/defc page-item-wrapper*
|
||||
{::mf/private true
|
||||
::mf/wrap-props false}
|
||||
[{:keys [page-id index is-deletable is-selected is-editing current-page-id]}]
|
||||
(let [page-ref (mf/with-memo [page-id]
|
||||
(make-page-ref page-id))
|
||||
page (mf/deref page-ref)]
|
||||
[:& page-item {:page page
|
||||
:index index
|
||||
:current-page-id current-page-id
|
||||
:deletable? deletable?
|
||||
:selected? selected?
|
||||
:editing? editing?}]))
|
||||
[:> page-item* {:page page
|
||||
:index index
|
||||
:current-page-id current-page-id
|
||||
:is-deletable is-deletable
|
||||
:is-selected is-selected
|
||||
:is-editing is-editing}]))
|
||||
|
||||
;; --- Pages List
|
||||
|
||||
@ -252,18 +270,23 @@
|
||||
(let [pages (:pages file)
|
||||
deletable? (> (count pages) 1)
|
||||
editing-page-id (mf/deref refs/editing-page-item)
|
||||
current-page-id (mf/use-ctx ctx/current-page-id)]
|
||||
[:ul {:class (stl/css :page-list)}
|
||||
selected-pages (mf/deref refs/selected-pages)
|
||||
current-page-id (mf/use-ctx ctx/current-page-id)
|
||||
;; When there is no explicit multi-selection, the current page
|
||||
;; is the selected one.
|
||||
selected-pages (if (seq selected-pages)
|
||||
selected-pages
|
||||
#{current-page-id})]
|
||||
[:ul
|
||||
[:> hooks/sortable-container* {}
|
||||
(for [[index page-id] (d/enumerate pages)]
|
||||
[:& page-item-wrapper
|
||||
{:page-id page-id
|
||||
:index index
|
||||
:deletable? deletable?
|
||||
:editing? (= page-id editing-page-id)
|
||||
:selected? (= page-id current-page-id)
|
||||
:current-page-id current-page-id
|
||||
:key page-id}])]]))
|
||||
[:> page-item-wrapper* {:page-id page-id
|
||||
:index index
|
||||
:is-deletable deletable?
|
||||
:is-editing (= page-id editing-page-id)
|
||||
:is-selected (contains? selected-pages page-id)
|
||||
:current-page-id current-page-id
|
||||
:key page-id}])]]))
|
||||
|
||||
;; --- Sitemap Toolbox
|
||||
|
||||
@ -276,7 +299,8 @@
|
||||
on-create (mf/use-fn
|
||||
(mf/deps file-id project-id)
|
||||
(fn [event]
|
||||
(st/emit! (dw/create-page {:file-id file-id :project-id project-id}))
|
||||
(st/emit! (dw/create-page {:file-id file-id
|
||||
:project-id project-id}))
|
||||
(-> event dom/get-current-target dom/blur!)))
|
||||
|
||||
read-only? (mf/use-ctx ctx/workspace-read-only?)
|
||||
@ -289,7 +313,7 @@
|
||||
:collapsed collapsed
|
||||
:on-collapsed on-toggle-collapsed
|
||||
:title (tr "workspace.sidebar.sitemap")
|
||||
:class (stl/css :title-spacing-sitemap)}
|
||||
:class (stl/css :sitemap-title)}
|
||||
|
||||
(if ^boolean read-only?
|
||||
(when ^boolean (:can-edit permissions)
|
||||
@ -297,12 +321,11 @@
|
||||
:size :small
|
||||
:content (tr "labels.view-only")}])
|
||||
[:> icon-button* {:variant "ghost"
|
||||
:class (stl/css :add-page)
|
||||
:aria-label (tr "workspace.sidebar.sitemap.add-page")
|
||||
:on-click on-create
|
||||
:icon i/add}])]
|
||||
|
||||
(when-not ^boolean collapsed
|
||||
[:div {:class (stl/css :tool-window-content)}
|
||||
[:> pages-list* {:file file :key (dm/str (:id file))}]])]))
|
||||
|
||||
[:div {:class (stl/css :sitemap-content)}
|
||||
[:> pages-list* {:key (dm/str (:id file))
|
||||
:file file}]])]))
|
||||
|
||||
@ -4,236 +4,155 @@
|
||||
//
|
||||
// Copyright (c) KALEIDOS INC Sucursal en España SL
|
||||
|
||||
@use "refactor/common-refactor.scss" as deprecated;
|
||||
@use "ds/_utils.scss" as *;
|
||||
@use "ds/_borders.scss" as *;
|
||||
@use "ds/_sizes.scss" as *;
|
||||
@use "ds/spacing.scss" as *;
|
||||
@use "ds/typography.scss" as t;
|
||||
@use "ds/mixins.scss" as *;
|
||||
|
||||
.sitemap {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: var(--height, deprecated.$s-200);
|
||||
inline-size: 100%;
|
||||
block-size: var(--height, $sz-200);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin-left: deprecated.$s-2;
|
||||
color: var(--title-foreground-color-hover);
|
||||
.sitemap-title {
|
||||
padding-inline-start: var(--sp-s);
|
||||
margin-block: var(--sp-s) var(--sp-xs);
|
||||
}
|
||||
|
||||
.resize-area {
|
||||
position: absolute;
|
||||
bottom: calc(-1 * deprecated.$s-8);
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: deprecated.$s-12;
|
||||
border-top: deprecated.$s-2 solid var(--resize-area-border-color);
|
||||
background-color: var(--resize-area-background-color);
|
||||
cursor: ns-resize;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--resize-area-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
.tool-window-content {
|
||||
.sitemap-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: calc(-38px + var(--height, deprecated.$s-200));
|
||||
width: var(--left-sidebar-width);
|
||||
inline-size: var(--left-sidebar-width);
|
||||
overflow: hidden auto;
|
||||
scrollbar-gutter: stable;
|
||||
|
||||
.element-list {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
.pages-list {
|
||||
width: 100%;
|
||||
max-height: deprecated.$s-152;
|
||||
margin-bottom: deprecated.$s-12;
|
||||
}
|
||||
.page-item {
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
.page-delete-button {
|
||||
--delete-button-display: none;
|
||||
}
|
||||
|
||||
.page-delete-button-icon {
|
||||
display: var(--delete-button-display);
|
||||
}
|
||||
|
||||
.page-element {
|
||||
@include deprecated.body-small-typography;
|
||||
|
||||
min-height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
min-block-size: $sz-32;
|
||||
inline-size: 100%;
|
||||
|
||||
&.dnd-over-top {
|
||||
border-top: deprecated.$s-1 solid var(--layer-row-foreground-color-drag);
|
||||
border-block-start: $b-1 solid var(--layer-row-foreground-color-drag);
|
||||
}
|
||||
|
||||
&.dnd-over-bot {
|
||||
border-bottom: deprecated.$s-1 solid var(--layer-row-foreground-color-drag);
|
||||
border-block-end: $b-1 solid var(--layer-row-foreground-color-drag);
|
||||
}
|
||||
|
||||
.dnd-over > .element-list-body {
|
||||
border: deprecated.$s-1 solid var(--layer-row-foreground-color-drag);
|
||||
}
|
||||
|
||||
.element-list-body {
|
||||
.page-item-body {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: deprecated.$s-32;
|
||||
width: 100%;
|
||||
padding: 0 deprecated.$s-12 0 0;
|
||||
block-size: $sz-32;
|
||||
inline-size: 100%;
|
||||
padding: 0 var(--sp-m) 0 0;
|
||||
transition: none;
|
||||
color: var(--layer-row-foreground-color);
|
||||
user-select: none;
|
||||
|
||||
.page-name {
|
||||
@include deprecated.text-ellipsis;
|
||||
|
||||
flex-grow: 1;
|
||||
padding-left: deprecated.$s-2;
|
||||
}
|
||||
|
||||
.page-icon {
|
||||
@include deprecated.flex-center;
|
||||
|
||||
padding: 0 deprecated.$s-4 0 deprecated.$s-8;
|
||||
}
|
||||
|
||||
.page-actions {
|
||||
height: deprecated.$s-32;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.element-name {
|
||||
@include deprecated.text-ellipsis;
|
||||
|
||||
color: var(--layer-row-foreground-color-focus);
|
||||
}
|
||||
|
||||
input.element-name {
|
||||
@include deprecated.text-ellipsis;
|
||||
@include deprecated.body-small-typography;
|
||||
@include deprecated.remove-input-style;
|
||||
|
||||
flex-grow: 1;
|
||||
height: deprecated.$s-28;
|
||||
max-width: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size)));
|
||||
padding-left: deprecated.$s-6;
|
||||
margin: 0;
|
||||
border-radius: deprecated.$br-8;
|
||||
border: deprecated.$s-1 solid var(--input-border-color-focus);
|
||||
color: var(--layer-row-foreground-color);
|
||||
&.separator {
|
||||
block-size: auto;
|
||||
min-block-size: var(--sp-xxxl);
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:active,
|
||||
&.on-drag {
|
||||
.element-list-body {
|
||||
color: var(--layer-row-foreground-color-drag);
|
||||
background-color: var(--layer-row-background-color-drag);
|
||||
.page-item-label {
|
||||
@include text-ellipsis;
|
||||
|
||||
.page-icon svg {
|
||||
stroke: var(--layer-row-foreground-color-drag);
|
||||
}
|
||||
}
|
||||
flex-grow: 1;
|
||||
padding-inline-start: var(--sp-xxs);
|
||||
}
|
||||
|
||||
&.selected,
|
||||
&.selected:hover {
|
||||
.element-list-body {
|
||||
color: var(--layer-row-foreground-color-selected);
|
||||
background-color: var(--layer-row-background-color-selected);
|
||||
box-shadow: deprecated.$s-16 deprecated.$s-0 deprecated.$s-0 deprecated.$s-0
|
||||
var(--layer-row-background-color-selected);
|
||||
|
||||
.page-icon svg {
|
||||
stroke: var(--layer-row-foreground-color-selected);
|
||||
}
|
||||
}
|
||||
.page-item-icon {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 var(--sp-xs) 0 var(--sp-s);
|
||||
}
|
||||
|
||||
&:hover,
|
||||
&.hover {
|
||||
.element-list-body {
|
||||
color: var(--layer-row-foreground-color-hover);
|
||||
background-color: var(--layer-row-background-color-hover);
|
||||
box-shadow: deprecated.$s-16 deprecated.$s-0 deprecated.$s-0 deprecated.$s-0
|
||||
var(--layer-row-background-color-hover);
|
||||
|
||||
.page-actions button {
|
||||
opacity: deprecated.$op-10;
|
||||
|
||||
--delete-button-display: initial;
|
||||
}
|
||||
|
||||
.page-icon svg {
|
||||
stroke: var(--layer-row-foreground-color-hover);
|
||||
}
|
||||
}
|
||||
.page-item-actions {
|
||||
block-size: $sz-32;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
.element-list-body {
|
||||
color: var(--layer-row-foreground-color-focus);
|
||||
border: deprecated.$s-1 solid var(--layer-row-border-color-focus);
|
||||
outline: none;
|
||||
.page-item-input {
|
||||
@include text-ellipsis;
|
||||
@include t.use-typography("body-small");
|
||||
|
||||
.page-actions button {
|
||||
opacity: deprecated.$op-10;
|
||||
}
|
||||
}
|
||||
background: none;
|
||||
outline: none;
|
||||
flex-grow: 1;
|
||||
block-size: $sz-28;
|
||||
max-inline-size: calc(var(--parent-size) - (var(--depth) * var(--layer-indentation-size)));
|
||||
padding-inline-start: $sz-6;
|
||||
margin: 0;
|
||||
border-radius: $br-8;
|
||||
border: $b-1 solid var(--input-border-color-focus);
|
||||
color: var(--layer-row-foreground-color);
|
||||
}
|
||||
|
||||
&:focus-within {
|
||||
.element-list-body {
|
||||
outline: none;
|
||||
|
||||
.page-actions button {
|
||||
opacity: deprecated.$op-10;
|
||||
}
|
||||
}
|
||||
&:active .page-item-body {
|
||||
color: var(--layer-row-foreground-color-drag);
|
||||
background-color: var(--layer-row-background-color-drag);
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
.element-list-body {
|
||||
color: var(--layer-row-foreground-color-hidden);
|
||||
background-color: var(--layer-row-background-color-hidden);
|
||||
opacity: deprecated.$op-7;
|
||||
&.selected .page-item-body,
|
||||
&.selected:hover .page-item-body {
|
||||
color: var(--layer-row-foreground-color-selected);
|
||||
background-color: var(--layer-row-background-color-selected);
|
||||
box-shadow: $sz-16 0 0 0 var(--layer-row-background-color-selected);
|
||||
}
|
||||
|
||||
.page-icon svg {
|
||||
stroke: var(--layer-row-foreground-color-hidden);
|
||||
}
|
||||
}
|
||||
&:hover .page-item-body,
|
||||
&.hover .page-item-body {
|
||||
color: var(--layer-row-foreground-color-hover);
|
||||
background-color: var(--layer-row-background-color-hover);
|
||||
box-shadow: $sz-16 0 0 0 var(--layer-row-background-color-hover);
|
||||
}
|
||||
|
||||
&:hover .page-delete-btn,
|
||||
&.hover .page-delete-btn {
|
||||
--delete-button-display: initial;
|
||||
}
|
||||
|
||||
&:focus .page-item-body {
|
||||
color: var(--layer-row-foreground-color-focus);
|
||||
border: $b-1 solid var(--layer-row-border-color-focus);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&:focus-within .page-item-body {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
&.separator:hover .page-item-body,
|
||||
&.separator.hover .page-item-body {
|
||||
color: var(--layer-row-foreground-color);
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
.element-list-body.separator-body {
|
||||
height: auto;
|
||||
min-height: var(--sp-xxxl);
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.page-separator {
|
||||
width: 100%;
|
||||
height: $b-1;
|
||||
.page-divider {
|
||||
inline-size: 100%;
|
||||
block-size: $b-1;
|
||||
margin: var(--sp-s);
|
||||
background-color: var(--color-background-quaternary);
|
||||
}
|
||||
|
||||
.page-element.separator:hover .element-list-body,
|
||||
.page-element.separator.hover .element-list-body {
|
||||
color: var(--layer-row-foreground-color);
|
||||
background-color: transparent;
|
||||
box-shadow: none;
|
||||
.page-delete-btn {
|
||||
--delete-button-display: none;
|
||||
}
|
||||
|
||||
.title-spacing-sitemap {
|
||||
padding-inline-start: deprecated.$s-8;
|
||||
margin-block: deprecated.$s-8 deprecated.$s-4;
|
||||
.page-delete-icon {
|
||||
display: var(--delete-button-display);
|
||||
}
|
||||
|
||||
@ -3926,6 +3926,14 @@ msgstr "Are you sure you want to delete this page?"
|
||||
msgid "modals.delete-page.title"
|
||||
msgstr "Delete page"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:712
|
||||
msgid "modals.delete-pages.body"
|
||||
msgstr "Are you sure you want to delete the selected pages?"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:711
|
||||
msgid "modals.delete-pages.title"
|
||||
msgstr "Delete pages"
|
||||
|
||||
#: src/app/main/ui/dashboard/project_menu.cljs:73
|
||||
msgid "modals.delete-project-confirm.accept"
|
||||
msgstr "Delete project"
|
||||
@ -6441,6 +6449,10 @@ msgstr "Your items are going to be named automatically as \"group name / item na
|
||||
msgid "workspace.assets.delete"
|
||||
msgstr "Delete"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:733
|
||||
msgid "workspace.assets.delete-pages"
|
||||
msgstr "Delete pages"
|
||||
|
||||
#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:74
|
||||
msgid "workspace.assets.delete-group"
|
||||
msgstr "Delete group"
|
||||
|
||||
@ -3810,6 +3810,14 @@ msgstr "¿Seguro que quieres borrar esta página?"
|
||||
msgid "modals.delete-page.title"
|
||||
msgstr "Borrar página"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:712
|
||||
msgid "modals.delete-pages.body"
|
||||
msgstr "¿Seguro que quieres borrar las páginas seleccionadas?"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:711
|
||||
msgid "modals.delete-pages.title"
|
||||
msgstr "Borrar páginas"
|
||||
|
||||
#: src/app/main/ui/dashboard/project_menu.cljs:73
|
||||
msgid "modals.delete-project-confirm.accept"
|
||||
msgstr "Eliminar proyecto"
|
||||
@ -6317,6 +6325,10 @@ msgstr ""
|
||||
msgid "workspace.assets.delete"
|
||||
msgstr "Borrar"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:733
|
||||
msgid "workspace.assets.delete-pages"
|
||||
msgstr "Borrar páginas"
|
||||
|
||||
#: src/app/main/ui/workspace/context_menu.cljs:721, src/app/main/ui/workspace/sidebar/assets/colors.cljs:262, src/app/main/ui/workspace/sidebar/assets/typographies.cljs:471, src/app/main/ui/workspace/sidebar/options/menus/typography.cljs:537
|
||||
msgid "workspace.assets.duplicate"
|
||||
msgstr "Duplicar"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user