0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-10 22:11:23 -05:00

Search and filter layers

This commit is contained in:
Pablo Alba 2022-03-15 20:03:13 +01:00 committed by Alonso Torres
parent 81adcd03fb
commit 3bae4839bd
7 changed files with 274 additions and 12 deletions

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="482 -308 500 500"><path d="M555-235a250 250 0 1 1 354 354 250 250 0 0 1-354-354zM732-95l-88-88-36 35 89 89-89 88 36 36 88-89 88 89 36-36-89-88 89-89-36-35z"/></svg>

After

Width:  |  Height:  |  Size: 214 B

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="299 -306 500 500"><path d="M799-306v89H299v-89zM696 194H402v-89h294zm52-206H351v-89h397z"/></svg>

After

Width:  |  Height:  |  Size: 147 B

View file

@ -295,3 +295,97 @@ span.element-name {
padding: 1px;
}
}
#layers {
.tool-window-bar {
display: flex;
justify-content: space-between;
height: 32px;
margin-top: 8px;
&.search {
.search-box {
border: 1px solid $color-primary;
border-radius: 4px;
height: 32px;
width: 100%;
display: flex;
align-items: center;
input {
border: 0;
width: 100%;
background-color: $color-gray-50;
color: $color-white;
font-size: 12px;
height: 16px;
}
span {
height: 16px;
overflow: hidden;
}
.filter,
.clear {
width: 35px;
&.active {
svg {
fill: $color-primary;
}
}
}
}
}
svg {
width: 16px;
height: 16px;
margin: 0 2px 0 5px;
cursor: pointer;
}
}
}
.active-filters {
margin-top: 5px;
line-height: 26px;
font-size: 11px;
margin: 0 0.5rem;
span {
background-color: $color-primary;
color: $color-black;
padding: 3px 5px;
margin: 0 2px;
border-radius: 4px;
cursor: pointer;
svg {
width: 7px;
height: 7px;
vertical-align: middle;
margin-left: 5px;
}
}
}
.filters-container {
position: absolute;
display: flex;
flex-direction: column;
top: 40px;
left: 8px;
background-color: $color-white;
color: $color-gray-50;
border-radius: 4px;
span {
padding: 10px 20px 10px 10px;
border-radius: 4px;
svg {
width: 16px;
height: 16px;
margin-right: 10px;
vertical-align: middle;
fill: $color-gray-30;
}
&:hover {
background-color: $color-primary-lighter;
}
}
}

View file

@ -52,6 +52,7 @@
(def easing-ease-in (icon-xref :easing-ease-in))
(def easing-ease-out (icon-xref :easing-ease-out))
(def easing-ease-in-out (icon-xref :easing-ease-in-out))
(def exclude (icon-xref :exclude))
(def exit (icon-xref :exit))
(def export (icon-xref :export))
(def eye (icon-xref :eye))
@ -67,6 +68,7 @@
(def grid-snap (icon-xref :grid-snap))
(def help (icon-xref :help))
(def icon-empty (icon-xref :icon-empty))
(def icon-filter (icon-xref :filter))
(def icon-list (icon-xref :icon-list))
(def icon-lock (icon-xref :icon-lock))
(def icon-set (icon-xref :icon-set))

View file

@ -304,13 +304,59 @@
{::mf/wrap-props false
::mf/wrap [mf/memo #(mf/throttle % 200)]}
[props]
(let [objects (-> (obj/get props "objects")
(let [search (obj/get props "search")
filters (obj/get props "filters")
filters (if (some #{:shape} filters)
(conj filters :rect :circle :path :bool)
filters)
objects (-> (obj/get props "objects")
(hooks/use-equal-memo))
objects (mf/use-memo
(mf/deps objects)
#(strip-objects objects))]
#(strip-objects objects))
reparented-objects (d/mapm (fn [_ val]
(assoc val :parent-id uuid/zero :shapes nil))
objects)
reparented-shapes (->> reparented-objects
keys
(filter #(not= uuid/zero %))
vec)
reparented-objects (update reparented-objects uuid/zero assoc :shapes reparented-shapes)
search-and-filters (mf/use-callback
(mf/deps search filters)
(fn [[id shape]]
(or
(= uuid/zero id)
(and
(str/includes? (str/lower (:name shape)) (str/lower search))
(or
(empty? filters)
(and
(some #{:component} filters)
(contains? shape :component-id))
(let [direct_filters (filter #{:frame :rect :circle :path :bool :image :text} filters)]
(some #{(:type shape)} direct_filters))
(and
(some #{:group} filters)
(and (= :group (:type shape))
(not (contains? shape :component-id))
(or (not (contains? shape :masked-group?)) (false? (:masked-group? shape)))))
(and
(some #{:mask} filters)
(true? (:masked-group? shape))))))))
objects (if (and (= "" search) (empty? filters))
objects
(into {} (filter search-and-filters
reparented-objects)))]
[:& layers-tree {:objects objects}]))
;; --- Layers Toolbox
(mf/defc layers-toolbox
@ -320,7 +366,11 @@
focus (mf/deref refs/workspace-focus-selected)
objects (hooks/with-focus-objects (:objects page) focus)
title (when (= 1 (count focus)) (get-in objects [(first focus) :name]))
filter-state (mf/use-state {:show-search-box false
:show-filters-menu false
:search-text ""
:active-filters {}})
on-scroll
(fn [event]
(let [target (dom/get-target event)
@ -328,26 +378,92 @@
frames (dom/get-elements-by-class "type-frame")
last-hidden-frame (->> frames
(filter #(< (- (:top (dom/get-bounding-rect %)) target-top) 0))
last)]
(doseq [frame frames]
last)]
(doseq [frame frames]
(dom/remove-class! frame "sticky"))
(when last-hidden-frame
(dom/add-class! last-hidden-frame "sticky"))))]
(dom/add-class! last-hidden-frame "sticky"))))
clear-search-text #(swap! filter-state assoc :search-text "")
update-search-text (fn [event]
(let [value (-> event dom/get-target dom/get-value)]
(swap! filter-state assoc :search-text value)))
toggle-search (fn []
(swap! filter-state assoc :search-text "")
(swap! filter-state assoc :active-filters {})
(swap! filter-state assoc :show-filters-menu false)
(swap! filter-state update :show-search-box not))
toggle-filters #(swap! filter-state update :show-filters-menu not)
remove-filter
(mf/use-callback
(mf/deps @filter-state)
(fn [key]
(fn [_]
(swap! filter-state update :active-filters dissoc key))))
add-filter
(mf/use-callback
(mf/deps @filter-state (:show-filters-menu @filter-state))
(fn [key value]
(fn [_]
(swap! filter-state update :active-filters assoc key value)
(toggle-filters))))]
[:div#layers.tool-window
(if (d/not-empty? focus)
[:div.tool-window-bar
[:div.focus-title
[:button.back-button
{:on-click #(st/emit! (dw/toggle-focus-mode))}
i/arrow-slide ]
i/arrow-slide]
[:span (or title (tr "workspace.focus.selection"))]
[:div.focus-mode (tr "workspace.focus.focus-mode")]]]
[:div.tool-window-bar
[:span (:name page)]])
(if (:show-search-box @filter-state)
[:*
[:div.tool-window-bar.search
[:span.search-box
[:span.filter {:on-click toggle-filters
:class (dom/classnames :active (or
(:show-filters-menu @filter-state)
(not-empty (:active-filters @filter-state))))}
i/icon-filter]
[:span
[:input {:on-change update-search-text
:value (:search-text @filter-state)
:auto-focus (:show-search-box @filter-state)
:placeholder (tr "workspace.sidebar.layers.search")}]]
(when (not (= "" (:search-text @filter-state)))
[:span.clear {:on-click clear-search-text} i/exclude])]
[:span {:on-click toggle-search} i/cross]
]
[:div.active-filters
(for [f (:active-filters @filter-state)]
[:span {:on-click (remove-filter (key f))}
(tr (val f)) i/cross])
]
(when (:show-filters-menu @filter-state)
[:div.filters-container
[:span{:on-click (add-filter :frame "workspace.sidebar.layers.frames")} i/artboard (tr "workspace.sidebar.layers.frames")]
[:span{:on-click (add-filter :group "workspace.sidebar.layers.groups")} i/folder (tr "workspace.sidebar.layers.groups")]
[:span{:on-click (add-filter :mask "workspace.sidebar.layers.masks")} i/mask (tr "workspace.sidebar.layers.masks")]
[:span{:on-click (add-filter :component "workspace.sidebar.layers.components")} i/component (tr "workspace.sidebar.layers.components")]
[:span{:on-click (add-filter :text "workspace.sidebar.layers.texts")} i/text (tr "workspace.sidebar.layers.texts")]
[:span{:on-click (add-filter :image "workspace.sidebar.layers.images")} i/image (tr "workspace.sidebar.layers.images")]
[:span{:on-click (add-filter :shape "workspace.sidebar.layers.shapes")} i/curve (tr "workspace.sidebar.layers.shapes")]])]
[:div.tool-window-bar
[:span (:name page)]
[:span {:on-click toggle-search} i/search]]))
[:div.tool-window-content {:on-scroll on-scroll}
[:& layers-tree-wrapper {:key (:id page)
:objects objects}]]]))
:objects objects
:search (:search-text @filter-state)
:filters (keys (:active-filters @filter-state))}]]]))

View file

@ -3377,6 +3377,30 @@ msgstr "History (%s)"
msgid "workspace.sidebar.layers"
msgstr "Layers"
msgid "workspace.sidebar.layers.search"
msgstr "Search layers"
msgid "workspace.sidebar.layers.frames"
msgstr "Artboards"
msgid "workspace.sidebar.layers.groups"
msgstr "Groups"
msgid "workspace.sidebar.layers.masks"
msgstr "Masks"
msgid "workspace.sidebar.layers.components"
msgstr "Components"
msgid "workspace.sidebar.layers.texts"
msgstr "Texts"
msgid "workspace.sidebar.layers.images"
msgstr "Images"
msgid "workspace.sidebar.layers.shapes"
msgstr "Shapes"
#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs
msgid "workspace.sidebar.options.svg-attrs.title"
msgstr "Imported SVG Attributes"

View file

@ -3391,6 +3391,30 @@ msgstr "Historial (%s)"
msgid "workspace.sidebar.layers"
msgstr "Capas"
msgid "workspace.sidebar.layers.search"
msgstr "Buscar capas"
msgid "workspace.sidebar.layers.frames"
msgstr "Paneles"
msgid "workspace.sidebar.layers.groups"
msgstr "Grupos"
msgid "workspace.sidebar.layers.masks"
msgstr "Máscaras"
msgid "workspace.sidebar.layers.components"
msgstr "Componentes"
msgid "workspace.sidebar.layers.texts"
msgstr "Textos"
msgid "workspace.sidebar.layers.images"
msgstr "Imágenes"
msgid "workspace.sidebar.layers.shapes"
msgstr "Formas"
#: src/app/main/ui/workspace/sidebar/options/menus/svg_attrs.cljs, src/app/main/ui/handoff/attributes/svg.cljs
msgid "workspace.sidebar.options.svg-attrs.title"
msgstr "Atributos del SVG Importado"