From c09fe467909b787739583d57ca9808ccc8c7a5a8 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 23 Mar 2020 11:33:31 +0100 Subject: [PATCH 1/7] :baby_chick: tabs component --- frontend/resources/styles/main.scss | 1 + .../styles/main/partials/sidebar.scss | 1 + .../styles/main/partials/tab-container.scss | 31 +++++++++++++++ .../main/ui/components/tab_container.cljs | 22 +++++++++++ .../main/ui/workspace/sidebar/libraries.cljs | 39 +++++++++++++++++++ 5 files changed, 94 insertions(+) create mode 100644 frontend/resources/styles/main/partials/tab-container.scss create mode 100644 frontend/src/uxbox/main/ui/components/tab_container.cljs create mode 100644 frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs diff --git a/frontend/resources/styles/main.scss b/frontend/resources/styles/main.scss index d6748e9472..1f545ff20e 100644 --- a/frontend/resources/styles/main.scss +++ b/frontend/resources/styles/main.scss @@ -67,6 +67,7 @@ @import 'main/partials/context-menu'; @import 'main/partials/debug-icons-preview'; @import 'main/partials/editable-label'; +@import 'main/partials/tab-container'; //################################################# // Resources diff --git a/frontend/resources/styles/main/partials/sidebar.scss b/frontend/resources/styles/main/partials/sidebar.scss index 1c0fe1aeac..afd37b7504 100644 --- a/frontend/resources/styles/main/partials/sidebar.scss +++ b/frontend/resources/styles/main/partials/sidebar.scss @@ -78,6 +78,7 @@ flex-wrap: wrap; overflow-y: auto; padding-bottom: $medium; + height: 100%; } &#layers { diff --git a/frontend/resources/styles/main/partials/tab-container.scss b/frontend/resources/styles/main/partials/tab-container.scss new file mode 100644 index 0000000000..03271d4729 --- /dev/null +++ b/frontend/resources/styles/main/partials/tab-container.scss @@ -0,0 +1,31 @@ + +.tab-container { + display: flex; + flex-direction: column; + height: 100%; + width: 100%; +} + +.tab-container-tabs { + background: $color-gray-60; + cursor: pointer; + display: flex; + flex-direction: row; + font-size: 12px; + height: 2.5rem; +} + +.tab-container-tab-title { + align-items: center; + background: $color-gray-60; + border-radius: 2px 2px 0px 0px; + color: $color-white; + display: flex; + justify-content: center; + margin: 0.5rem 0.25rem 0 0.25rem ; + width: 100%; + + &.current{ + background: $color-gray-50; + } +} diff --git a/frontend/src/uxbox/main/ui/components/tab_container.cljs b/frontend/src/uxbox/main/ui/components/tab_container.cljs new file mode 100644 index 0000000000..35c212290a --- /dev/null +++ b/frontend/src/uxbox/main/ui/components/tab_container.cljs @@ -0,0 +1,22 @@ +(ns uxbox.main.ui.components.tab-container + (:require [rumext.alpha :as mf])) + +(mf/defc tab-element + [{:keys [children id title]}] + [:div.tab-element + [:div.tab-element-content children]]) + +(mf/defc tab-container + [{:keys [children selected]}] + (.log js/console (map #(-> % .-props .-title) children)) + (let [first-id (-> children first .-props .-id) + state (mf/use-state {:selected first-id})] + [:div.tab-container + [:div.tab-container-tabs + (for [tab children] + [:div.tab-container-tab-title + {:on-click #(swap! state assoc :selected (-> tab .-props .-id)) + :class (when (= (:selected @state) (-> tab .-props .-id)) "current")} + (-> tab .-props .-title)])] + [:div.tab-container-content + (filter #(= (:selected @state) (-> % .-props .-id)) children)]])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs new file mode 100644 index 0000000000..c08a7b4cbc --- /dev/null +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs @@ -0,0 +1,39 @@ +;; 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/. +;; +;; This Source Code Form is "Incompatible With Secondary Licenses", as +;; defined by the Mozilla Public License, v. 2.0. +;; +;; Copyright (c) 2020 UXBOX Labs SL + +(ns uxbox.main.ui.workspace.sidebar.libraries + (:require + [lentes.core :as l] + [rumext.alpha :as mf] + [uxbox.common.data :as d] + [uxbox.builtins.icons :as i] + [uxbox.main.data.workspace :as dw] + [uxbox.main.refs :as refs] + [uxbox.main.store :as st] + [uxbox.main.ui.keyboard :as kbd] + [uxbox.main.ui.shapes.icon :as icon] + [uxbox.main.ui.workspace.sortable :refer [use-sortable]] + [uxbox.util.dom :as dom] + [uxbox.util.uuid :as uuid] + [uxbox.util.i18n :as i18n :refer [t]] + [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]])) + +(mf/defc libraries-toolbox + [] + (let [locale (i18n/use-locale)] + [:div#libraries.tool-window + [:div.tool-window-bar + [:div "Libraries"] + [:div "All libraries"]] + [:div.tool-window-content + [:& tab-container {:selected :icons :on-change-tab #(println "Change tab")} + [:& tab-element {:id :icons :title "Icons"} + [:p "ICONS TAB"]] + [:& tab-element {:id :images :title "Images"} + [:p "IMAGES TAB"]]]]])) From 4102dca55c87db13366962b45474642417830a67 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 23 Mar 2020 15:23:29 +0100 Subject: [PATCH 2/7] :penguin: adds layout for libraries sidebar --- frontend/resources/styles/main.scss | 3 +- .../styles/main/partials/sidebar-sitemap.scss | 2 - .../styles/main/partials/sidebar.scss | 29 +++--- .../styles/main/partials/tab-container.scss | 12 +++ .../main/partials/workspace-libraries.scss | 89 +++++++++++++++++++ frontend/src/uxbox/main/data/workspace.cljs | 23 +++-- .../main/ui/components/tab_container.cljs | 15 ++-- frontend/src/uxbox/main/ui/workspace.cljs | 6 +- .../uxbox/main/ui/workspace/left_toolbar.cljs | 6 +- .../src/uxbox/main/ui/workspace/sidebar.cljs | 9 +- .../main/ui/workspace/sidebar/libraries.cljs | 32 +++++-- frontend/src/uxbox/util/data.cljs | 1 + 12 files changed, 187 insertions(+), 40 deletions(-) create mode 100644 frontend/resources/styles/main/partials/workspace-libraries.scss diff --git a/frontend/resources/styles/main.scss b/frontend/resources/styles/main.scss index 1f545ff20e..cb43312a55 100644 --- a/frontend/resources/styles/main.scss +++ b/frontend/resources/styles/main.scss @@ -42,8 +42,9 @@ //################################################# @import 'main/partials/main-bar'; -@import 'main/partials/workspace-bar'; @import 'main/partials/workspace'; +@import 'main/partials/workspace-bar'; +@import 'main/partials/workspace-libraries'; @import 'main/partials/tool-bar'; @import 'main/partials/project-bar'; @import 'main/partials/sidebar'; diff --git a/frontend/resources/styles/main/partials/sidebar-sitemap.scss b/frontend/resources/styles/main/partials/sidebar-sitemap.scss index b2c63b6f48..00eae95a61 100644 --- a/frontend/resources/styles/main/partials/sidebar-sitemap.scss +++ b/frontend/resources/styles/main/partials/sidebar-sitemap.scss @@ -6,8 +6,6 @@ // Copyright (c) 2015-2016 Juan de la Cruz .sitemap { - max-height: 180px; - .tool-window-bar { .add-page { diff --git a/frontend/resources/styles/main/partials/sidebar.scss b/frontend/resources/styles/main/partials/sidebar.scss index afd37b7504..9bf242cced 100644 --- a/frontend/resources/styles/main/partials/sidebar.scss +++ b/frontend/resources/styles/main/partials/sidebar.scss @@ -11,7 +11,7 @@ height: 100%; position: fixed; right: 0; - width: 230px; + width: 15rem; z-index: 10; &.settings-bar-left { @@ -20,10 +20,22 @@ .settings-bar-inside { align-items: center; - display: flex; + display: grid; + + &[data-layout*='layers'] { + grid-template-rows: 30% 70%; + } + + &[data-layout*='libraries'] { + grid-template-rows: 100%; + } + + &[data-layout*='layers'][data-layout*='libraries'] { + grid-template-rows: 15% 25% 60%; + } + flex-direction: column; - overflow-y: auto; - overflow-x: hidden; + overflow: hidden; padding-top: 40px; height: 100%; @@ -33,12 +45,14 @@ flex-direction: column; flex: 1; width: 100%; + height: 100%; .tool-window-bar { align-items: center; display: flex; flex-shrink: 0; padding: $small; + overflow: hidden; svg { fill: $color-gray-20; @@ -80,13 +94,6 @@ padding-bottom: $medium; height: 100%; } - - &#layers { - padding-bottom: 30px; - } - } - } - } diff --git a/frontend/resources/styles/main/partials/tab-container.scss b/frontend/resources/styles/main/partials/tab-container.scss index 03271d4729..601f394ea8 100644 --- a/frontend/resources/styles/main/partials/tab-container.scss +++ b/frontend/resources/styles/main/partials/tab-container.scss @@ -13,6 +13,7 @@ flex-direction: row; font-size: 12px; height: 2.5rem; + padding: 0 0.25rem; } .tab-container-tab-title { @@ -29,3 +30,14 @@ background: $color-gray-50; } } + +.tab-container-content { + flex: 1; + height: 100%; + max-height: 100%; + overflow: hidden; +} + +.tab-element, .tab-element-content { + height: 100%; +} diff --git a/frontend/resources/styles/main/partials/workspace-libraries.scss b/frontend/resources/styles/main/partials/workspace-libraries.scss new file mode 100644 index 0000000000..a64d2db4cf --- /dev/null +++ b/frontend/resources/styles/main/partials/workspace-libraries.scss @@ -0,0 +1,89 @@ +.library-tab { + display: flex; + flex-direction: column; + height: 100%; + padding: 0.25rem; +} + +.library-tab-content { + display: grid; + flex-direction: row; + flex-wrap: wrap; + padding: 0.25rem; + height: 100%; + overflow-y: scroll; + + .icons-tab & { + grid-template-columns: repeat(3, 1fr); + } + + .images-tab & { + grid-template-columns: repeat(2, 1fr); + } +} + +.library-tab-element { + border-radius: 4px; + border: 1px solid #1F1F1F; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); + box-sizing: border-box; + cursor: pointer; + display: flex; + margin: 0.25rem; + overflow: hidden; + position: relative; + text-align: center; + + & svg, & img { + height: auto; + margin: auto; + max-height: 100%; + max-width: 100%; + width: auto; + } + + &:hover { + border-color: $color-primary; + + & .library-tab-element-name { + display: inline; + } + } + + .icons-tab & { + background: $color-white; + color: $color-black; + height: 4rem; + width: 4rem; + padding: $size-3; + } + + .images-tab & { + height: 4rem; + width: 6.2rem; + color: $color-white; + padding: $size-2 0; + } +} + +.library-tab-element-name { + display: none; + position: absolute; + font-size: 9px; + bottom: 0; + left: 0; + width: 100%; +} + +.library-tab-libraries { + margin: 0.5rem; + width: 90%; + padding: 0.5rem; + box-sizing: border-box; + color: white; + font-size: 12px; +} + +.library-tab-libraries-item { + padding: 1rem; +} diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index f1016a9792..19ad538102 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -269,7 +269,9 @@ (declare initialize-alignment) -(def default-layout #{:sitemap :layers :element-options :rules}) +#_(def default-layout #{:sitemap :layers :element-options :rules}) +(def default-layout #{:libraries :rules}) + (def workspace-default {:zoom 1 @@ -770,16 +772,20 @@ ;; --- Toggle layout flag (defn toggle-layout-flag - [flag] - (us/verify keyword? flag) + [& flags] + ;; Verify all? + #_(us/verify keyword? flag) (ptk/reify ::toggle-layout-flag ptk/UpdateEvent (update [_ state] - (update state :workspace-layout - (fn [flags] - (if (contains? flags flag) - (disj flags flag) - (conj flags flag))))))) + (let [reduce-fn + (fn [state flag] + (update state :workspace-layout + (fn [flags] + (if (contains? flags flag) + (disj flags flag) + (conj flags flag)))))] + (reduce reduce-fn state flags))))) ;; --- Tooltip @@ -1103,6 +1109,7 @@ (rx/empty)))))) + ;; --- Toggle shape's selection status (selected or deselected) (defn select-shape diff --git a/frontend/src/uxbox/main/ui/components/tab_container.cljs b/frontend/src/uxbox/main/ui/components/tab_container.cljs index 35c212290a..6344efba2c 100644 --- a/frontend/src/uxbox/main/ui/components/tab_container.cljs +++ b/frontend/src/uxbox/main/ui/components/tab_container.cljs @@ -7,16 +7,21 @@ [:div.tab-element-content children]]) (mf/defc tab-container - [{:keys [children selected]}] + [{:keys [children selected on-change-tab]}] (.log js/console (map #(-> % .-props .-title) children)) (let [first-id (-> children first .-props .-id) - state (mf/use-state {:selected first-id})] + state (mf/use-state {:selected first-id}) + selected (or selected (:selected @state)) + handle-select (fn [tab] + (let [id (-> tab .-props .-id)] + (swap! state assoc :selected id) + (on-change-tab id)))] [:div.tab-container [:div.tab-container-tabs (for [tab children] [:div.tab-container-tab-title - {:on-click #(swap! state assoc :selected (-> tab .-props .-id)) - :class (when (= (:selected @state) (-> tab .-props .-id)) "current")} + {:on-click (partial handle-select tab) + :class (when (= selected (-> tab .-props .-id)) "current")} (-> tab .-props .-title)])] [:div.tab-container-content - (filter #(= (:selected @state) (-> % .-props .-id)) children)]])) + (filter #(= selected (-> % .-props .-id)) children)]])) diff --git a/frontend/src/uxbox/main/ui/workspace.cljs b/frontend/src/uxbox/main/ui/workspace.cljs index 92d132cd34..1e3f1e5765 100644 --- a/frontend/src/uxbox/main/ui/workspace.cljs +++ b/frontend/src/uxbox/main/ui/workspace.cljs @@ -60,10 +60,8 @@ (mf/defc workspace-content [{:keys [page file layout] :as params}] (let [frame (mf/use-ref nil) - left-sidebar? (not (empty? (keep layout [:layers :sitemap - :document-history]))) - right-sidebar? (not (empty? (keep layout [:icons :drawtools - :element-options]))) + left-sidebar? (not (empty? (keep layout [:layers :sitemap :document-history :libraries]))) + right-sidebar? (not (empty? (keep layout [:icons :drawtools :element-options]))) classes (classnames :no-tool-bar-right (not right-sidebar?) :no-tool-bar-left (not left-sidebar?))] diff --git a/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs b/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs index 29048d0c15..3e3cdb29ec 100644 --- a/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs +++ b/frontend/src/uxbox/main/ui/workspace/left_toolbar.cljs @@ -68,10 +68,12 @@ [:li.tooltip.tooltip-right {:alt "Layers" :class (when (contains? layout :layers) "selected") - :on-click #(st/emit! (dw/toggle-layout-flag :layers))} + :on-click #(st/emit! (dw/toggle-layout-flag :layers :sitemap))} i/layers] [:li.tooltip.tooltip-right - {:alt "Libraries"} + {:alt "Libraries" + :class (when (contains? layout :libraries) "selected") + :on-click #(st/emit! (dw/toggle-layout-flag :libraries))} i/icon-set] [:li.tooltip.tooltip-right {:alt "History"} diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar.cljs index bd18b669ea..f3fa789802 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar.cljs @@ -11,11 +11,13 @@ (ns uxbox.main.ui.workspace.sidebar (:require [rumext.alpha :as mf] + [cuerdas.core :as str] [uxbox.main.ui.workspace.sidebar.history :refer [history-toolbox]] [uxbox.main.ui.workspace.sidebar.icons :refer [icons-toolbox]] [uxbox.main.ui.workspace.sidebar.layers :refer [layers-toolbox]] [uxbox.main.ui.workspace.sidebar.options :refer [options-toolbox]] - [uxbox.main.ui.workspace.sidebar.sitemap :refer [sitemap-toolbox]])) + [uxbox.main.ui.workspace.sidebar.sitemap :refer [sitemap-toolbox]] + [uxbox.main.ui.workspace.sidebar.libraries :refer [libraries-toolbox]])) ;; --- Left Sidebar (Component) @@ -24,12 +26,15 @@ [{:keys [layout page file] :as props}] [:aside.settings-bar.settings-bar-left [:div.settings-bar-inside + {:data-layout (str/join "," layout)} (when (contains? layout :sitemap) [:& sitemap-toolbox {:file file :page page}]) (when (contains? layout :document-history) [:& history-toolbox]) (when (contains? layout :layers) - [:& layers-toolbox {:page page}])]]) + [:& layers-toolbox {:page page}]) + (when (contains? layout :libraries) + [:& libraries-toolbox])]]) ;; --- Right Sidebar (Component) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs index c08a7b4cbc..05a3e8d3fc 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs @@ -24,6 +24,30 @@ [uxbox.util.i18n :as i18n :refer [t]] [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]])) +(mf/defc library-tab [] + [:div.library-tab.icons-tab + [:select.library-tab-libraries + [:option.library-tab-libraries-item "Material design"] + [:option.library-tab-libraries-item "Icon library 1"] + [:option.library-tab-libraries-item "Icon library 2"]] + [:div.library-tab-content + (for [_ (range 0 200)] + [:div.library-tab-element + i/trash + [:span.library-tab-element-name "my-icon.svg"]])]]) + +(mf/defc images-tab [] + [:div.library-tab.images-tab + [:select.library-tab-libraries + [:option.library-tab-libraries-item "Material design"] + [:option.library-tab-libraries-item "Icon library 1"] + [:option.library-tab-libraries-item "Icon library 2"]] + [:div.library-tab-content + (for [_ (range 0 200)] + [:div.library-tab-element + [:img {:src "https://www.placecage.com/c/200/300"}] + [:span.library-tab-element-name "my-icon.svg"]])]]) + (mf/defc libraries-toolbox [] (let [locale (i18n/use-locale)] @@ -32,8 +56,6 @@ [:div "Libraries"] [:div "All libraries"]] [:div.tool-window-content - [:& tab-container {:selected :icons :on-change-tab #(println "Change tab")} - [:& tab-element {:id :icons :title "Icons"} - [:p "ICONS TAB"]] - [:& tab-element {:id :images :title "Images"} - [:p "IMAGES TAB"]]]]])) + [:& tab-container {} + [:& tab-element {:id :icons :title "Icons"} [:& library-tab]] + [:& tab-element {:id :images :title "Images"} [:& images-tab]]]]])) diff --git a/frontend/src/uxbox/util/data.cljs b/frontend/src/uxbox/util/data.cljs index 4dd2035f5c..61145306d8 100644 --- a/frontend/src/uxbox/util/data.cljs +++ b/frontend/src/uxbox/util/data.cljs @@ -205,3 +205,4 @@ ;; (if (::some-interrupt (ex-data e#)) ;; nil ;; (throw e#))))))) + From 57d633b1d2079a183fdcb85644b771befe17f12e Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 24 Mar 2020 14:31:15 +0100 Subject: [PATCH 3/7] :frog: integration with backend data --- backend/src/uxbox/services/queries/colors.clj | 8 +- backend/src/uxbox/services/queries/icons.clj | 10 +- backend/src/uxbox/services/queries/images.clj | 8 +- .../src/uxbox/services/queries/projects.clj | 21 ++++ backend/src/uxbox/services/queries/teams.clj | 13 +- common/uxbox/common/spec.cljc | 2 +- .../styles/main/partials/color-palette.scss | 45 +++++-- .../main/partials/workspace-libraries.scss | 36 +++++- frontend/src/uxbox/main/data/library.cljs | 53 ++++---- frontend/src/uxbox/main/data/workspace.cljs | 43 +++++-- frontend/src/uxbox/main/ui.cljs | 8 +- .../main/ui/components/tab_container.cljs | 2 +- .../src/uxbox/main/ui/dashboard/grid.cljs | 7 +- .../src/uxbox/main/ui/dashboard/library.cljs | 12 +- frontend/src/uxbox/main/ui/workspace.cljs | 8 +- .../uxbox/main/ui/workspace/colorpalette.cljs | 22 ++-- .../main/ui/workspace/sidebar/libraries.cljs | 119 +++++++++++++----- 17 files changed, 301 insertions(+), 116 deletions(-) diff --git a/backend/src/uxbox/services/queries/colors.clj b/backend/src/uxbox/services/queries/colors.clj index b1deb3dfb7..afe78209d5 100644 --- a/backend/src/uxbox/services/queries/colors.clj +++ b/backend/src/uxbox/services/queries/colors.clj @@ -50,7 +50,7 @@ (sq/defquery ::color-libraries [{:keys [profile-id team-id]}] (db/with-atomic [conn db/pool] - (teams/check-edition-permissions! conn profile-id team-id) + (teams/check-read-permissions! conn profile-id team-id) (db/query conn [sql:libraries team-id]))) @@ -66,7 +66,7 @@ [{:keys [profile-id id]}] (db/with-atomic [conn db/pool] (p/let [lib (retrieve-library conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) + (teams/check-read-permissions! conn profile-id (:team-id lib)) lib))) (def ^:private sql:single-library @@ -94,7 +94,7 @@ [{:keys [profile-id library-id] :as params}] (db/with-atomic [conn db/pool] (p/let [lib (retrieve-library conn library-id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) + (teams/check-read-permissions! conn profile-id (:team-id lib)) (retrieve-colors conn library-id)))) (def ^:private sql:colors @@ -123,7 +123,7 @@ [{:keys [profile-id id] :as params}] (db/with-atomic [conn db/pool] (p/let [color (retrieve-color conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id color)) + (teams/check-read-permissions! conn profile-id (:team-id color)) color))) (def ^:private sql:single-color diff --git a/backend/src/uxbox/services/queries/icons.clj b/backend/src/uxbox/services/queries/icons.clj index b1922f37fe..deddf82b6e 100644 --- a/backend/src/uxbox/services/queries/icons.clj +++ b/backend/src/uxbox/services/queries/icons.clj @@ -56,8 +56,10 @@ (sq/defquery ::icon-libraries [{:keys [profile-id team-id]}] + (println profile-id) + (println team-id) (db/with-atomic [conn db/pool] - (teams/check-edition-permissions! conn profile-id team-id) + (teams/check-read-permissions! conn profile-id team-id) (db/query conn [sql:libraries team-id]))) @@ -73,7 +75,7 @@ [{:keys [profile-id id]}] (db/with-atomic [conn db/pool] (p/let [lib (retrieve-library conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) + (teams/check-read-permissions! conn profile-id (:team-id lib)) lib))) (def ^:private sql:single-library @@ -101,7 +103,7 @@ [{:keys [profile-id library-id] :as params}] (db/with-atomic [conn db/pool] (p/let [lib (retrieve-library conn library-id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) + (teams/check-read-permissions! conn profile-id (:team-id lib)) (-> (retrieve-icons conn library-id) (p/then' (fn [rows] (mapv decode-row rows))))))) @@ -131,7 +133,7 @@ [{:keys [profile-id id] :as params}] (db/with-atomic [conn db/pool] (p/let [icon (retrieve-icon conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id icon)) + (teams/check-read-permissions! conn profile-id (:team-id icon)) (decode-row icon)))) (def ^:private sql:single-icon diff --git a/backend/src/uxbox/services/queries/images.clj b/backend/src/uxbox/services/queries/images.clj index a690a5cc1d..c6cd106a8f 100644 --- a/backend/src/uxbox/services/queries/images.clj +++ b/backend/src/uxbox/services/queries/images.clj @@ -40,7 +40,7 @@ (sq/defquery ::image-libraries [{:keys [profile-id team-id]}] (db/with-atomic [conn db/pool] - (teams/check-edition-permissions! conn profile-id team-id) + (teams/check-read-permissions! conn profile-id team-id) (db/query conn [sql:libraries team-id]))) @@ -55,7 +55,7 @@ [{:keys [profile-id id]}] (db/with-atomic [conn db/pool] (p/let [lib (retrieve-library conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) + (teams/check-read-permissions! conn profile-id (:team-id lib)) lib))) (def ^:private sql:single-library @@ -86,7 +86,7 @@ [{:keys [profile-id library-id] :as params}] (db/with-atomic [conn db/pool] (p/let [lib (retrieve-library conn library-id)] - (teams/check-edition-permissions! conn profile-id (:team-id lib)) + (teams/check-read-permissions! conn profile-id (:team-id lib)) (-> (retrieve-images conn library-id) (p/then' (fn [rows] (->> rows @@ -120,7 +120,7 @@ [{:keys [profile-id id] :as params}] (db/with-atomic [conn db/pool] (p/let [img (retrieve-image conn id)] - (teams/check-edition-permissions! conn profile-id (:team-id img)) + (teams/check-read-permissions! conn profile-id (:team-id img)) (-> img (images/resolve-urls :path :uri) (images/resolve-urls :thumb-path :thumb-uri))))) diff --git a/backend/src/uxbox/services/queries/projects.clj b/backend/src/uxbox/services/queries/projects.clj index ffd1ffb4dd..45d495b4cc 100644 --- a/backend/src/uxbox/services/queries/projects.clj +++ b/backend/src/uxbox/services/queries/projects.clj @@ -47,16 +47,37 @@ where team_id = $2 order by modified_at desc") +(def ^:private sql:project-by-id + "select p.* + from project as p + inner join project_profile_rel as ppr on (ppr.project_id = p.id) + where ppr.profile_id = $1 + and p.id = $2 + and p.deleted_at is null + and (ppr.is_admin = true or + ppr.is_owner = true or + ppr.can_edit = true)") + (s/def ::team-id ::us/uuid) (s/def ::profile-id ::us/uuid) +(s/def ::project-id ::us/uuid) (s/def ::projects-by-team (s/keys :req-un [::profile-id ::team-id])) +(s/def ::project-by-id + (s/keys :req-un [::profile-id ::project-id])) + (defn projects-by-team [profile-id team-id] (db/query db/pool [sql:projects profile-id team-id])) +(defn project-by-id [profile-id project-id] + (db/query-one db/pool [sql:project-by-id profile-id project-id])) + (sq/defquery ::projects-by-team [{:keys [profile-id team-id]}] (projects-by-team profile-id team-id)) +(sq/defquery ::project-by-id + [{:keys [profile-id project-id]}] + (project-by-id profile-id project-id)) diff --git a/backend/src/uxbox/services/queries/teams.clj b/backend/src/uxbox/services/queries/teams.clj index efce8203a3..b3b32d58d3 100644 --- a/backend/src/uxbox/services/queries/teams.clj +++ b/backend/src/uxbox/services/queries/teams.clj @@ -40,5 +40,14 @@ (ex/raise :type :validation :code :not-authorized)))))) - - +(defn check-read-permissions! + [conn profile-id team-id] + (-> (db/query-one conn [sql:team-permissions profile-id team-id]) + (p/then' (fn [row] + (when-not (or (:can-edit row) + (:is-admin row) + (:is-owner row) + ;; We can read global-project owned items + (= team-id #uuid "00000000-0000-0000-0000-000000000000")) + (ex/raise :type :validation + :code :not-authorized)))))) diff --git a/common/uxbox/common/spec.cljc b/common/uxbox/common/spec.cljc index 10af336cf7..179d3bac6a 100644 --- a/common/uxbox/common/spec.cljc +++ b/common/uxbox/common/spec.cljc @@ -24,7 +24,7 @@ #"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") (def uuid-rx - #"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$") + #"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$") ;; --- Conformers diff --git a/frontend/resources/styles/main/partials/color-palette.scss b/frontend/resources/styles/main/partials/color-palette.scss index 3039e78e0f..cb84e31565 100644 --- a/frontend/resources/styles/main/partials/color-palette.scss +++ b/frontend/resources/styles/main/partials/color-palette.scss @@ -11,21 +11,23 @@ background-color: $color-gray-50; border-top: 1px solid $color-gray-60; display: flex; - padding: 1rem; position: absolute; bottom: 0; left: 0; width: 100%; z-index: 11; - .right-arrow, - .left-arrow { - cursor: pointer; + + & .right-arrow, + & .left-arrow { + cursor: pointer; + svg { fill: $color-gray-light; - height: 30px; + height: 1rem; margin: 0 .5rem; - width: 30px; + width: 1rem; } + &:hover { svg { fill: $color-gray-darker; @@ -35,12 +37,21 @@ display: none; } } + + .left-arrow { - transform: rotate(180deg); + transform: rotate(180deg); + padding-top: 10px; } + &.fade-out-down { @include animation(0,.5s,fadeOutDown); } + + &.left-sidebar-open { + left: 280px; + width: calc(100% - 280px); + } } .color-palette-actions { @@ -48,7 +59,12 @@ flex-direction: column; flex-shrink: 0; margin-right: .5rem; - width: 200px; + + border: 1px solid #1F1F1F; + align-self: stretch; + padding: 0.5rem; + justify-content: center; + .color-palette-buttons { align-items: center; display: flex; @@ -56,6 +72,15 @@ } } +.color-palette-actions-button { + cursor: pointer; + & svg { + width: 1rem; + height: 1rem; + fill: #AFB2BF; + } +} + .btn-palette { align-items: center; border: 2px solid $color-gray-lighter; @@ -90,6 +115,7 @@ display: flex; overflow: hidden; width: 100%; + padding: 0.25rem; } .color-palette-inside { @@ -106,15 +132,14 @@ display: flex; flex-direction: column; flex-shrink: 0; - margin: 0 10px; position: relative; flex-basis: 66px; + .color { background-color: $color-gray-lighter; border: 2px solid $color-gray-60; border-radius: 50%; flex-shrink: 0; - margin-bottom: .4rem; padding: 1.5rem; } .color-text { diff --git a/frontend/resources/styles/main/partials/workspace-libraries.scss b/frontend/resources/styles/main/partials/workspace-libraries.scss index a64d2db4cf..b48f513adc 100644 --- a/frontend/resources/styles/main/partials/workspace-libraries.scss +++ b/frontend/resources/styles/main/partials/workspace-libraries.scss @@ -1,3 +1,35 @@ +.libraries-window-bar { + display: grid; + grid-template-columns: repeat(2, 50%); + padding: 0.5rem; + align-items: center; +} + +.libraries-window-bar-title { + color: #F0F0F0; +} + +.libraries-window-bar-options { + font-size: 12px; + display: flex; + justify-content: space-between; + padding: 0 0.5rem; + + button { + border: none; + padding: 0; + margin: 0; + background: transparent; + cursor: pointer; + } + & svg { + width: 0.5rem; + height: 0.5rem; + fill: #F0F0F0; + transform: rotate(90deg); + } +} + .library-tab { display: flex; flex-direction: column; @@ -10,7 +42,6 @@ flex-direction: row; flex-wrap: wrap; padding: 0.25rem; - height: 100%; overflow-y: scroll; .icons-tab & { @@ -80,8 +111,9 @@ width: 90%; padding: 0.5rem; box-sizing: border-box; - color: white; + color: #AFB2BF; font-size: 12px; + border: 1px solid #7c7c7c; } .library-tab-libraries-item { diff --git a/frontend/src/uxbox/main/data/library.cljs b/frontend/src/uxbox/main/data/library.cljs index ff9805d121..6b7ac9d70c 100644 --- a/frontend/src/uxbox/main/data/library.cljs +++ b/frontend/src/uxbox/main/data/library.cljs @@ -14,30 +14,33 @@ [uxbox.util.router :as r] [uxbox.util.uuid :as uuid])) +(defn initialize-workspace-libraries [] + ()) ;; Retrieve libraries (declare retrieve-libraries-result) (defn retrieve-libraries - [type team-id] - (s/assert ::us/uuid team-id) - (let [method (case type - :icons :icon-libraries - :images :image-libraries - :palettes :color-libraries)] - (ptk/reify ::retrieve-libraries - ptk/WatchEvent - (watch [_ state stream] - (->> (rp/query! method {:team-id team-id}) - (rx/map (partial retrieve-libraries-result type))))))) + ([type] (retrieve-libraries type uuid/zero)) + ([type team-id] + (s/assert ::us/uuid team-id) + (let [method (case type + :icons :icon-libraries + :images :image-libraries + :palettes :color-libraries)] + (ptk/reify ::retrieve-libraries + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/query! method {:team-id team-id}) + (rx/map (partial retrieve-libraries-result type team-id)))))))) -(defn retrieve-libraries-result [type result] +(defn retrieve-libraries-result [type team-id result] (ptk/reify ::retrieve-libraries-result ptk/UpdateEvent (update [_ state] (-> state - (assoc-in [:library type] result))))) + (assoc-in [:library type team-id] result))))) ;; Retrieve library data @@ -78,23 +81,23 @@ :images :create-image-library :palettes :create-color-library)] (->> (rp/mutation! method {:team-id team-id - :name name}) - (rx/map (partial create-library-result type))))))) + :name name}) + (rx/map (partial create-library-result type team-id))))))) (defn create-library-result - [type result] + [type team-id result] (ptk/reify ::create-library-result ptk/UpdateEvent (update [_ state] (-> state - (update-in [:library type] #(into [result] %)))))) + (update-in [:library type team-id] #(into [result] %)))))) ;; Rename library (declare rename-library-result) (defn rename-library - [type library-id name] + [type team-id library-id name] (ptk/reify ::rename-library ptk/WatchEvent (watch [_ state stream] @@ -104,10 +107,10 @@ :palettes :rename-color-library)] (->> (rp/mutation! method {:id library-id :name name}) - (rx/map #(rename-library-result type library-id name))))))) + (rx/map #(rename-library-result type team-id library-id name))))))) (defn rename-library-result - [type library-id name] + [type team-id library-id name] (ptk/reify ::rename-library-result ptk/UpdateEvent (update [_ state] @@ -118,14 +121,14 @@ (update-fn [libraries] (map change-name libraries))] (-> state - (update-in [:library type] update-fn)))))) + (update-in [:library type team-id] update-fn)))))) ;; Delete library (declare delete-library-result) (defn delete-library - [type library-id] + [type team-id library-id] (ptk/reify ::delete-library ptk/UpdateEvent (update [_ state] @@ -139,17 +142,17 @@ :images :delete-image-library :palettes :delete-color-library)] (->> (rp/mutation! method {:id library-id}) - (rx/map #(delete-library-result type library-id))))))) + (rx/map #(delete-library-result type team-id library-id))))))) (defn delete-library-result - [type library-id] + [type team-id library-id] (ptk/reify ::create-library-result ptk/UpdateEvent (update [_ state] (let [update-fn (fn [libraries] (filterv #(not= library-id (:id %)) libraries))] (-> state - (update-in [:library type] update-fn)))))) + (update-in [:library type team-id] update-fn)))))) ;; Delete library item diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 19ad538102..838c67f461 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -66,6 +66,7 @@ (declare fetch-users) (declare fetch-images) +(declare fetch-project) (declare handle-who) (declare handle-pointer-update) (declare handle-pointer-send) @@ -270,7 +271,7 @@ (declare initialize-alignment) #_(def default-layout #{:sitemap :layers :element-options :rules}) -(def default-layout #{:libraries :rules}) +(def default-layout #{:libraries :rules :colorpalette}) (def workspace-default @@ -296,7 +297,8 @@ (defn initialize "Initialize the workspace state." - [file-id page-id] + [project-id file-id page-id] + (us/verify ::us/uuid project-id) (us/verify ::us/uuid file-id) (us/verify ::us/uuid page-id) (ptk/reify ::initialize @@ -307,9 +309,11 @@ (rx/merge (rx/of (fetch-file-with-users file-id) (fetch-pages file-id) - (fetch-images file-id)) + (fetch-images file-id) + (fetch-project project-id)) (->> (rx/zip (rx/filter (ptk/type? ::pages-fetched) stream) - (rx/filter (ptk/type? ::file-fetched) stream)) + (rx/filter (ptk/type? ::file-fetched) stream) + (rx/filter (ptk/type? ::project-fetched) stream)) (rx/take 1) (rx/do (fn [_] (uxbox.util.timers/schedule 500 #(reset! st/loader false)))) @@ -357,7 +361,8 @@ (rx/of (initialize-page-persistence page-id))))) (defn finalize - [file-id page-id] + [project-id file-id page-id] + (us/verify ::us/uuid project-id) (us/verify ::us/uuid file-id) (us/verify ::us/uuid page-id) (ptk/reify ::finalize @@ -495,6 +500,7 @@ (s/def ::data ::cp/data) (s/def ::file ::dd/file) +(s/def ::project ::dd/project) (s/def ::page (s/keys :req-un [::id ::name @@ -548,6 +554,25 @@ state users)))) +;; --- Fetch Project data +(declare project-fetched) + +(defn fetch-project + [id] + (us/verify ::us/uuid id) + (ptk/reify ::fetch-project + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/query :project-by-id {:project-id id}) + (rx/map project-fetched))))) + +(defn project-fetched + [project] + (us/verify ::project project) + (ptk/reify ::project-fetched + ptk/UpdateEvent + (update [_ state] + (assoc state :workspace-project project)))) ;; --- Fetch Pages @@ -1931,7 +1956,8 @@ (ptk/reify ::go-to-page ptk/WatchEvent (watch [_ state stream] - (let [file-id (get-in state [:workspace-page :file-id]) + (let [project-id (get-in state [:workspace-project :id]) + file-id (get-in state [:workspace-page :file-id]) path-params {:file-id file-id} query-params {:page-id page-id}] (rx/of (rt/nav :workspace path-params query-params)))))) @@ -1942,8 +1968,9 @@ (ptk/reify ::go-to-file ptk/WatchEvent (watch [_ state stream] - (let [page-ids (get-in state [:files file-id :pages]) - path-params {:file-id file-id} + (let [project-id (get-in state [:workspace-project :id]) + page-ids (get-in state [:files file-id :pages]) + path-params {:project-id project-id :file-id file-id} query-params {:page-id (first page-ids)}] (rx/of (rt/nav :workspace path-params query-params)))))) diff --git a/frontend/src/uxbox/main/ui.cljs b/frontend/src/uxbox/main/ui.cljs index 312cba7593..38d44ccf9c 100644 --- a/frontend/src/uxbox/main/ui.cljs +++ b/frontend/src/uxbox/main/ui.cljs @@ -73,7 +73,7 @@ ]]] - ["/workspace/:file-id" :workspace]]) + ["/workspace/:project-id/:file-id" :workspace]]) (mf/defc app-error [{:keys [error] :as props}] @@ -119,9 +119,11 @@ (mf/element dashboard #js {:route route}) :workspace - (let [file-id (uuid (get-in route [:params :path :file-id])) + (let [project-id (uuid (get-in route [:params :path :project-id])) + file-id (uuid (get-in route [:params :path :file-id])) page-id (uuid (get-in route [:params :query :page-id]))] - [:& workspace/workspace {:file-id file-id + [:& workspace/workspace {:project-id project-id + :file-id file-id :page-id page-id :key file-id}]) nil))) diff --git a/frontend/src/uxbox/main/ui/components/tab_container.cljs b/frontend/src/uxbox/main/ui/components/tab_container.cljs index 6344efba2c..2c081ccf69 100644 --- a/frontend/src/uxbox/main/ui/components/tab_container.cljs +++ b/frontend/src/uxbox/main/ui/components/tab_container.cljs @@ -15,7 +15,7 @@ handle-select (fn [tab] (let [id (-> tab .-props .-id)] (swap! state assoc :selected id) - (on-change-tab id)))] + (when on-change-tab (on-change-tab id))))] [:div.tab-container [:div.tab-container-tabs (for [tab children] diff --git a/frontend/src/uxbox/main/ui/dashboard/grid.cljs b/frontend/src/uxbox/main/ui/dashboard/grid.cljs index b933c3b7e1..eb9146adf7 100644 --- a/frontend/src/uxbox/main/ui/dashboard/grid.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/grid.cljs @@ -41,8 +41,9 @@ :edition false}) locale (i18n/use-locale) on-navigate #(st/emit! (rt/nav :workspace - {:file-id (:id file)} - {:page-id (first (:pages file))})) + {:project-id (:project-id file) + :file-id (:id file)} + {:page-id (first (:pages file))})) delete-fn #(st/emit! nil (dsh/delete-file (:id file))) on-delete #(do (dom/stop-propagation %) @@ -75,7 +76,7 @@ [:h3 (:name file)]) [:& grid-item-metadata {:modified-at (:modified-at file)}]] [:div.project-th-actions {:class (classnames - :force-display (:menu-open @local))} + :force-display (:menu-open @local))} ;; [:div.project-th-icon.pages ;; i/page ;; #_[:span (:total-pages project)]] diff --git a/frontend/src/uxbox/main/ui/dashboard/library.cljs b/frontend/src/uxbox/main/ui/dashboard/library.cljs index a3d52aadc6..7db1aa7229 100644 --- a/frontend/src/uxbox/main/ui/dashboard/library.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/library.cljs @@ -122,7 +122,7 @@ (dlib/retrieve-libraries :icons (:id item)) (st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))} [:& editable-label {:value (:name item) - :on-change #(st/emit! (dlib/rename-library section library-id %))}] + :on-change #(st/emit! (dlib/rename-library section team-id library-id %))}] ])]])) (mf/defc library-top-menu @@ -136,7 +136,7 @@ [:& editable-label {:edit (:editing-name @state) :on-change #(do (stop-editing) - (st/emit! (dlib/rename-library section library-id %))) + (st/emit! (dlib/rename-library section team-id library-id %))) :on-cancel #(swap! state assoc :editing-name false) :class-name "library-top-menu-current-element-name" :value (:name selected)}] @@ -155,7 +155,7 @@ (modal/show! confirm-dialog {:on-accept #(do - (st/emit! (dlib/delete-library section library-id)) + (st/emit! (dlib/delete-library section team-id library-id)) (st/emit! (rt/nav path {:team-id team-id}))) :message "Are you sure you want to delete this library?" :accept-text "Delete"})))]]}]] @@ -301,8 +301,8 @@ :message "Are you sure you want to delete this color?" :accept-text "Delete"}))]]}]]]))) -(defn libraries-ref [section] - (-> (comp (l/key :library) (l/key section)) +(defn libraries-ref [section team-id] + (-> (comp (l/key :library) (l/key section) (l/key team-id)) (l/derive st/state))) (defn selected-items-ref [library-id] @@ -316,7 +316,7 @@ (mf/defc library-page [{:keys [team-id library-id section]}] (let [state (mf/use-state {:selected #{}}) - libraries (mf/deref (libraries-ref section)) + libraries (mf/deref (libraries-ref section team-id)) items (mf/deref (selected-items-ref library-id)) last-deleted-library (mf/deref last-deleted-library-ref) selected-library (first (filter #(= (:id %) library-id) libraries))] diff --git a/frontend/src/uxbox/main/ui/workspace.cljs b/frontend/src/uxbox/main/ui/workspace.cljs index 1e3f1e5765..403d2a5b32 100644 --- a/frontend/src/uxbox/main/ui/workspace.cljs +++ b/frontend/src/uxbox/main/ui/workspace.cljs @@ -67,7 +67,7 @@ :no-tool-bar-left (not left-sidebar?))] [:* (when (:colorpalette layout) - [:& colorpalette]) + [:& colorpalette {:left-sidebar? left-sidebar?}]) [:main.main-content [:& context-menu {}] @@ -99,13 +99,13 @@ (mf/defc workspace - [{:keys [file-id page-id] :as props}] + [{:keys [project-id file-id page-id] :as props}] (-> (mf/deps file-id page-id) (mf/use-effect (fn [] - (st/emit! (dw/initialize file-id page-id)) - #(st/emit! (dw/finalize file-id page-id))))) + (st/emit! (dw/initialize project-id file-id page-id)) + #(st/emit! (dw/finalize project-id file-id page-id))))) (-> (mf/deps file-id) (mf/use-effect diff --git a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs index 742717575b..3085ad7bcb 100644 --- a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs +++ b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs @@ -42,10 +42,10 @@ :on-click select-color} [:span.color {:style {:background color}}] [:span.color-text color] - [:span.color-text rgb-color]])) + #_[:span.color-text rgb-color]])) (mf/defc palette - [{:keys [colls] :as props}] + [{:keys [colls left-sidebar?] :as props}] (let [local (mf/use-state {}) colls (->> colls (filter :id) @@ -97,9 +97,10 @@ (mf/use-effect nil after-render) - [:div.color-palette + [:div.color-palette {:class (when left-sidebar? "left-sidebar-open")} [:div.color-palette-actions - [:select.input-select {:on-change select-coll + [:div.color-palette-actions-button i/actions] + #_[:select.input-select {:on-change select-coll :default-value (pr-str (:id coll))} (for [item colls] [:option {:key (:id item) :value (pr-str (:id item))} @@ -116,14 +117,17 @@ :style {:position "relative" :width (str (* 86 (count (:colors coll))) "px") :right (str (* 86 offset) "px")}} - (for [color (:colors coll)] - [:& palette-item {:color color :key color}])]] + #_(for [color (:colors coll)] + [:& palette-item {:color color :key color}]) + (for [color (range 0 20)] + [:& palette-item {:color "#FFFF00" :key color}])]] [:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide] - [:span.close-palette {:on-click close} i/close]])) + #_[:span.close-palette {:on-click close} i/close]])) (mf/defc colorpalette - [props] + [{:keys [left-sidebar?]}] (let [colls (mf/deref collections-iref)] #_(mf/use-effect #(st/emit! (udc/fetch-collections))) - [:& palette {:colls (vals colls)}])) + [:& palette {:left-sidebar? left-sidebar? + :colls (vals colls)}])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs index 05a3e8d3fc..df6884f136 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs @@ -10,6 +10,7 @@ (ns uxbox.main.ui.workspace.sidebar.libraries (:require [lentes.core :as l] + [cuerdas.core :as str] [rumext.alpha :as mf] [uxbox.common.data :as d] [uxbox.builtins.icons :as i] @@ -22,40 +23,98 @@ [uxbox.util.dom :as dom] [uxbox.util.uuid :as uuid] [uxbox.util.i18n :as i18n :refer [t]] - [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]])) + [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]] + [uxbox.main.data.library :as dlib])) -(mf/defc library-tab [] - [:div.library-tab.icons-tab - [:select.library-tab-libraries - [:option.library-tab-libraries-item "Material design"] - [:option.library-tab-libraries-item "Icon library 1"] - [:option.library-tab-libraries-item "Icon library 2"]] - [:div.library-tab-content - (for [_ (range 0 200)] - [:div.library-tab-element - i/trash - [:span.library-tab-element-name "my-icon.svg"]])]]) +(def project-ref + (-> (l/key :workspace-project) + (l/derive st/state))) -(mf/defc images-tab [] - [:div.library-tab.images-tab - [:select.library-tab-libraries - [:option.library-tab-libraries-item "Material design"] - [:option.library-tab-libraries-item "Icon library 1"] - [:option.library-tab-libraries-item "Icon library 2"]] - [:div.library-tab-content - (for [_ (range 0 200)] - [:div.library-tab-element - [:img {:src "https://www.placecage.com/c/200/300"}] - [:span.library-tab-element-name "my-icon.svg"]])]]) +(defn libraries-ref [section] + (-> (comp (l/key :library) (l/key section)) + (l/derive st/state))) + +(defn selected-items-ref [library-id] + (-> (comp (l/key :library) (l/key :selected-items) (l/key library-id)) + (l/derive st/state))) + +(mf/defc icons-tab [{:keys [libraries]}] + (when (and libraries (-> libraries count (> 0))) + (let [state (mf/use-state {:selected-library (-> libraries first :id)})] + (mf/use-effect {:fn (fn [] + (st/emit! (dlib/retrieve-library-data :icons (:selected-library @state)))) + :deps (mf/deps (:selected-library @state))}) + + [:div.library-tab.icons-tab + [:select.input-select.library-tab-libraries + {:on-change #(swap! state assoc :selected-library (-> % dom/get-target dom/get-value))} + (for [library libraries] + [:option.library-tab-libraries-item + {:key (:id library) + :value (:id library)} + (:name library)])] + [:div.library-tab-content + (let [items (mf/deref (selected-items-ref (:selected-library @state)))] + (for [item items] + [:div.library-tab-element + {:key (:id item)} + [:svg {:view-box (->> item :metadata :view-box (str/join " ")) + :width (-> item :metadata :width) + :height (-> item :metadat :height) + :dangerouslySetInnerHTML {:__html (:content item)}}] + [:span.library-tab-element-name (:name item)]]))]]))) + +(mf/defc images-tab [{:keys [libraries]}] + (when (and libraries (-> libraries count (> 0))) + (let [state (mf/use-state {:selected-library (-> libraries first :id)})] + (mf/use-effect {:fn (fn [] + (st/emit! (dlib/retrieve-library-data :images (:selected-library @state)))) + :deps (mf/deps (:selected-library @state))}) + + [:div.library-tab.images-tab + [:select.input-select.library-tab-libraries + {:on-change #(swap! state assoc :selected-library (-> % dom/get-target dom/get-value))} + (for [library libraries] + [:option.library-tab-libraries-item + {:key (:id library) + :value (:id library)} + (:name library)])] + [:div.library-tab-content + (let [items (mf/deref (selected-items-ref (:selected-library @state)))] + (for [item items] + [:div.library-tab-element + {:key (:id item)} + [:img {:src (:thumb-uri item)}] + [:span.library-tab-element-name (:name item)]]))]]))) (mf/defc libraries-toolbox - [] - (let [locale (i18n/use-locale)] + [{:keys [key]}] + (let [team-id (-> project-ref mf/deref :team-id) + locale (i18n/use-locale)] + (mf/use-effect {:fn (fn [] + (st/emit! (dlib/retrieve-libraries :icons)) + (st/emit! (dlib/retrieve-libraries :images))) + :deps (mf/deps key)}) + (mf/use-effect {:fn (fn [] + (when team-id + (do + (st/emit! (dlib/retrieve-libraries :icons team-id)) + (st/emit! (dlib/retrieve-libraries :images team-id))))) + :deps (mf/deps team-id)}) [:div#libraries.tool-window - [:div.tool-window-bar - [:div "Libraries"] - [:div "All libraries"]] + [:div.libraries-window-bar + [:div.libraries-window-bar-title "Libraries"] + [:div.libraries-window-bar-options + "All libraries" + [:button {:type "button"} + i/arrow-slide]]] [:div.tool-window-content [:& tab-container {} - [:& tab-element {:id :icons :title "Icons"} [:& library-tab]] - [:& tab-element {:id :images :title "Images"} [:& images-tab]]]]])) + [:& tab-element + {:id :icons :title "Icons"} + [:& icons-tab {:libraries (-> (libraries-ref :icons) mf/deref vals flatten) }]] + + [:& tab-element + {:id :images :title "Images"} + [:& images-tab {:libraries (-> (libraries-ref :images) mf/deref vals flatten)}]]]]])) + From decd3e34430e7db30626eab5bcd26d0704a77799 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 26 Mar 2020 08:36:08 +0100 Subject: [PATCH 4/7] :fire: changes to effects --- .../main/partials/workspace-libraries.scss | 1 + .../main/ui/components/tab_container.cljs | 4 +-- .../main/ui/workspace/sidebar/libraries.cljs | 32 +++++++++---------- 3 files changed, 19 insertions(+), 18 deletions(-) diff --git a/frontend/resources/styles/main/partials/workspace-libraries.scss b/frontend/resources/styles/main/partials/workspace-libraries.scss index b48f513adc..9415c2fc39 100644 --- a/frontend/resources/styles/main/partials/workspace-libraries.scss +++ b/frontend/resources/styles/main/partials/workspace-libraries.scss @@ -107,6 +107,7 @@ } .library-tab-libraries { + background-color: #303236; margin: 0.5rem; width: 90%; padding: 0.5rem; diff --git a/frontend/src/uxbox/main/ui/components/tab_container.cljs b/frontend/src/uxbox/main/ui/components/tab_container.cljs index 2c081ccf69..78accbbcc9 100644 --- a/frontend/src/uxbox/main/ui/components/tab_container.cljs +++ b/frontend/src/uxbox/main/ui/components/tab_container.cljs @@ -8,7 +8,6 @@ (mf/defc tab-container [{:keys [children selected on-change-tab]}] - (.log js/console (map #(-> % .-props .-title) children)) (let [first-id (-> children first .-props .-id) state (mf/use-state {:selected first-id}) selected (or selected (:selected @state)) @@ -20,7 +19,8 @@ [:div.tab-container-tabs (for [tab children] [:div.tab-container-tab-title - {:on-click (partial handle-select tab) + {:key (str "tab-" (-> tab .-props .-id)) + :on-click (partial handle-select tab) :class (when (= selected (-> tab .-props .-id)) "current")} (-> tab .-props .-title)])] [:div.tab-container-content diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs index df6884f136..a61463c1d6 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs @@ -41,9 +41,8 @@ (mf/defc icons-tab [{:keys [libraries]}] (when (and libraries (-> libraries count (> 0))) (let [state (mf/use-state {:selected-library (-> libraries first :id)})] - (mf/use-effect {:fn (fn [] - (st/emit! (dlib/retrieve-library-data :icons (:selected-library @state)))) - :deps (mf/deps (:selected-library @state))}) + (mf/use-effect (mf/deps (:selected-library @state)) + #(st/emit! (dlib/retrieve-library-data :icons (:selected-library @state)))) [:div.library-tab.icons-tab [:select.input-select.library-tab-libraries @@ -67,9 +66,8 @@ (mf/defc images-tab [{:keys [libraries]}] (when (and libraries (-> libraries count (> 0))) (let [state (mf/use-state {:selected-library (-> libraries first :id)})] - (mf/use-effect {:fn (fn [] - (st/emit! (dlib/retrieve-library-data :images (:selected-library @state)))) - :deps (mf/deps (:selected-library @state))}) + (mf/use-effect (mf/deps (:selected-library @state)) + #(st/emit! (dlib/retrieve-library-data :images (:selected-library @state)))) [:div.library-tab.images-tab [:select.input-select.library-tab-libraries @@ -91,16 +89,17 @@ [{:keys [key]}] (let [team-id (-> project-ref mf/deref :team-id) locale (i18n/use-locale)] - (mf/use-effect {:fn (fn [] - (st/emit! (dlib/retrieve-libraries :icons)) - (st/emit! (dlib/retrieve-libraries :images))) - :deps (mf/deps key)}) - (mf/use-effect {:fn (fn [] - (when team-id - (do - (st/emit! (dlib/retrieve-libraries :icons team-id)) - (st/emit! (dlib/retrieve-libraries :images team-id))))) - :deps (mf/deps team-id)}) + (mf/use-effect + (mf/deps key) + #(do + (st/emit! (dlib/retrieve-libraries :icons)) + (st/emit! (dlib/retrieve-libraries :images)))) + (mf/use-effect + (mf/deps team-id) + #(when team-id + (do + (st/emit! (dlib/retrieve-libraries :icons team-id)) + (st/emit! (dlib/retrieve-libraries :images team-id))))) [:div#libraries.tool-window [:div.libraries-window-bar [:div.libraries-window-bar-title "Libraries"] @@ -118,3 +117,4 @@ {:id :images :title "Images"} [:& images-tab {:libraries (-> (libraries-ref :images) mf/deref vals flatten)}]]]]])) + From 5cd8e850340ca51f1111b0ef84e573d82bf0ed07 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 26 Mar 2020 14:19:27 +0100 Subject: [PATCH 5/7] :space_invader: changes libraries types, reworked color palette --- frontend/resources/images/icons/tick.svg | 3 + .../styles/main/partials/color-palette.scss | 35 ++-- .../styles/main/partials/context-menu.scss | 20 ++- .../styles/main/partials/sidebar.scss | 3 +- .../main/partials/workspace-libraries.scss | 5 + frontend/src/uxbox/main/data/colors.cljs | 2 +- frontend/src/uxbox/main/data/icons.cljs | 2 +- frontend/src/uxbox/main/data/images.cljs | 2 +- frontend/src/uxbox/main/data/library.cljs | 10 +- frontend/src/uxbox/main/data/workspace.cljs | 4 +- .../main/ui/components/context_menu.cljs | 14 +- .../src/uxbox/main/ui/dashboard/library.cljs | 6 +- .../uxbox/main/ui/workspace/colorpalette.cljs | 156 +++++++++--------- .../main/ui/workspace/sidebar/libraries.cljs | 59 +++++-- 14 files changed, 193 insertions(+), 128 deletions(-) create mode 100644 frontend/resources/images/icons/tick.svg diff --git a/frontend/resources/images/icons/tick.svg b/frontend/resources/images/icons/tick.svg new file mode 100644 index 0000000000..88d3be0aa3 --- /dev/null +++ b/frontend/resources/images/icons/tick.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/styles/main/partials/color-palette.scss b/frontend/resources/styles/main/partials/color-palette.scss index cb84e31565..42a7f3a589 100644 --- a/frontend/resources/styles/main/partials/color-palette.scss +++ b/frontend/resources/styles/main/partials/color-palette.scss @@ -52,24 +52,30 @@ left: 280px; width: calc(100% - 280px); } + + & .context-menu-items { + bottom: 1.5rem; + top: initial; + min-width: 10rem; + } } .color-palette-actions { - display: flex; - flex-direction: column; - flex-shrink: 0; - margin-right: .5rem; - - border: 1px solid #1F1F1F; - align-self: stretch; - padding: 0.5rem; - justify-content: center; - - .color-palette-buttons { - align-items: center; + align-self: stretch; + border: 1px solid #1F1F1F; + cursor: pointer; display: flex; - justify-content: space-around; - } + flex-direction: column; + flex-shrink: 0; + justify-content: center; + margin-right: .5rem; + padding: 0.5rem; + + .color-palette-buttons { + align-items: center; + display: flex; + justify-content: space-around; + } } .color-palette-actions-button { @@ -115,6 +121,7 @@ display: flex; overflow: hidden; width: 100%; + height: 4.8rem; padding: 0.25rem; } diff --git a/frontend/resources/styles/main/partials/context-menu.scss b/frontend/resources/styles/main/partials/context-menu.scss index e6acef56b2..58735efef9 100644 --- a/frontend/resources/styles/main/partials/context-menu.scss +++ b/frontend/resources/styles/main/partials/context-menu.scss @@ -17,7 +17,9 @@ border-radius: $br-small; box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); left: -$size-4; + max-height: 30rem; min-width: 7rem; + overflow: auto; position: absolute; top: $size-3; } @@ -27,9 +29,25 @@ display: block; font-size: $fs12; padding: $size-2 $size-4; + white-space: nowrap; &:hover { color: $color-black; - background: $color-primary-lighter; + background-color: $color-primary-lighter; } } + +.context-menu.is-selectable { + & .context-menu-action { + padding-left: 1.5rem; + } + + & .context-menu-item.is-selected .context-menu-action { + background-image: url(/images/icons/tick.svg); + background-repeat: no-repeat; + background-position: 5% 48%; + background-size: 10px; + font-weight: bold; + } +} + diff --git a/frontend/resources/styles/main/partials/sidebar.scss b/frontend/resources/styles/main/partials/sidebar.scss index 9bf242cced..0200bfda2e 100644 --- a/frontend/resources/styles/main/partials/sidebar.scss +++ b/frontend/resources/styles/main/partials/sidebar.scss @@ -21,7 +21,8 @@ .settings-bar-inside { align-items: center; display: grid; - + grid-template-columns: 100%; + &[data-layout*='layers'] { grid-template-rows: 30% 70%; } diff --git a/frontend/resources/styles/main/partials/workspace-libraries.scss b/frontend/resources/styles/main/partials/workspace-libraries.scss index 9415c2fc39..fe4a8c2f31 100644 --- a/frontend/resources/styles/main/partials/workspace-libraries.scss +++ b/frontend/resources/styles/main/partials/workspace-libraries.scss @@ -3,6 +3,11 @@ grid-template-columns: repeat(2, 50%); padding: 0.5rem; align-items: center; + + & .context-menu-items { + left: initial; + right: 0; + } } .libraries-window-bar-title { diff --git a/frontend/src/uxbox/main/data/colors.cljs b/frontend/src/uxbox/main/data/colors.cljs index 812844ab01..5639ab5e85 100644 --- a/frontend/src/uxbox/main/data/colors.cljs +++ b/frontend/src/uxbox/main/data/colors.cljs @@ -266,4 +266,4 @@ ptk/UpdateEvent (update [_ state] (-> state - (update-in [:library :selected-items library-id] #(into [item] %) ))))) + (update-in [:library-items :palettes library-id] #(into [item] %) ))))) diff --git a/frontend/src/uxbox/main/data/icons.cljs b/frontend/src/uxbox/main/data/icons.cljs index 132f881fb6..ee0d5717f2 100644 --- a/frontend/src/uxbox/main/data/icons.cljs +++ b/frontend/src/uxbox/main/data/icons.cljs @@ -203,7 +203,7 @@ (update [_ state] (let [{:keys [id] :as item} (assoc item :type :icon)] (-> state - (update-in [:library :selected-items library-id] #(into [item] %))))))) + (update-in [:library-items :icons library-id] #(into [item] %))))))) ;; ;; --- Icon Persisted ;; diff --git a/frontend/src/uxbox/main/data/images.cljs b/frontend/src/uxbox/main/data/images.cljs index d9a5614a4f..38dfcb8c9f 100644 --- a/frontend/src/uxbox/main/data/images.cljs +++ b/frontend/src/uxbox/main/data/images.cljs @@ -395,5 +395,5 @@ ptk/UpdateEvent (update [_ state] (-> state - (update-in [:library :selected-items library-id] #(into [item] %)))))) + (update-in [:library-items :images library-id] #(into [item] %)))))) diff --git a/frontend/src/uxbox/main/data/library.cljs b/frontend/src/uxbox/main/data/library.cljs index 6b7ac9d70c..f7596e980d 100644 --- a/frontend/src/uxbox/main/data/library.cljs +++ b/frontend/src/uxbox/main/data/library.cljs @@ -56,15 +56,15 @@ :images :images :palettes :colors)] (->> (rp/query! method {:library-id library-id}) - (rx/map (partial retrieve-library-data-result library-id))))))) + (rx/map (partial retrieve-library-data-result type library-id))))))) (defn retrieve-library-data-result - [library-id data] + [type library-id data] (ptk/reify ::retrieve-library-data-result ptk/UpdateEvent (update [_ state] (-> state - (assoc-in [:library :selected-items library-id] data))))) + (assoc-in [:library-items type library-id] data))))) ;; Create library @@ -178,7 +178,7 @@ (let [update-fn (fn [items] (filterv #(not= item-id (:id %)) items))] (-> state - (update-in [:library :selected-items library-id] update-fn)))))) + (update-in [:library-items type library-id] update-fn)))))) ;; Batch delete @@ -207,4 +207,4 @@ update-fn (fn [items] (filterv #(not (item-ids-set (:id %))) items))] (-> state - (update-in [:library :selected-items library-id] update-fn)))))) + (update-in [:library-items type library-id] update-fn)))))) diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 838c67f461..f633fb0bd9 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -270,9 +270,7 @@ (declare initialize-alignment) -#_(def default-layout #{:sitemap :layers :element-options :rules}) -(def default-layout #{:libraries :rules :colorpalette}) - +(def default-layout #{:sitemap :layers :element-options :rules}) (def workspace-default {:zoom 1 diff --git a/frontend/src/uxbox/main/ui/components/context_menu.cljs b/frontend/src/uxbox/main/ui/components/context_menu.cljs index 2c7d85a2fd..1023ff301c 100644 --- a/frontend/src/uxbox/main/ui/components/context_menu.cljs +++ b/frontend/src/uxbox/main/ui/components/context_menu.cljs @@ -3,7 +3,8 @@ [rumext.alpha :as mf] [goog.object :as gobj] [uxbox.main.ui.components.dropdown :refer [dropdown-container]] - [uxbox.util.uuid :as uuid])) + [uxbox.util.uuid :as uuid] + [uxbox.util.data :refer [classnames]])) (mf/defc context-menu {::mf/wrap-props false} @@ -13,12 +14,17 @@ (assert (vector? (gobj/get props "options")) "missing `options` prop") (let [open? (gobj/get props "show") - options (gobj/get props "options")] + options (gobj/get props "options") + is-selectable (gobj/get props "selectable") + selected (gobj/get props "selected")] + (println "selected" selected) (when open? [:> dropdown-container props - [:div.context-menu {:class (when open? "is-open")} + [:div.context-menu {:class (classnames :is-open open? + :is-selectable is-selectable)} [:ul.context-menu-items (for [[action-name action-handler] options] - [:li.context-menu-item {:key action-name} + [:li.context-menu-item {:class (classnames :is-selected (and selected (= action-name selected))) + :key action-name} [:a.context-menu-action {:on-click action-handler} action-name]])]]]))) diff --git a/frontend/src/uxbox/main/ui/dashboard/library.cljs b/frontend/src/uxbox/main/ui/dashboard/library.cljs index 7db1aa7229..d432d6eb56 100644 --- a/frontend/src/uxbox/main/ui/dashboard/library.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/library.cljs @@ -305,8 +305,8 @@ (-> (comp (l/key :library) (l/key section) (l/key team-id)) (l/derive st/state))) -(defn selected-items-ref [library-id] - (-> (comp (l/key :library) (l/key :selected-items) (l/key library-id)) +(defn selected-items-ref [section library-id] + (-> (comp (l/key :library-items) (l/key section) (l/key library-id)) (l/derive st/state))) (def last-deleted-library-ref @@ -317,7 +317,7 @@ [{:keys [team-id library-id section]}] (let [state (mf/use-state {:selected #{}}) libraries (mf/deref (libraries-ref section team-id)) - items (mf/deref (selected-items-ref library-id)) + items (mf/deref (selected-items-ref section library-id)) last-deleted-library (mf/deref last-deleted-library-ref) selected-library (first (filter #(= (:id %) library-id) libraries))] diff --git a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs index 3085ad7bcb..2ba95faa24 100644 --- a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs +++ b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs @@ -13,16 +13,26 @@ [uxbox.builtins.icons :as i] [uxbox.main.data.colors :as udc] [uxbox.main.data.workspace :as udw] + [uxbox.main.data.library :as dlib] [uxbox.main.store :as st] [uxbox.main.ui.keyboard :as kbd] [uxbox.util.color :refer [hex->rgb]] [uxbox.util.data :refer [read-string seek]] - [uxbox.util.dom :as dom])) + [uxbox.util.dom :as dom] + [uxbox.main.ui.components.context-menu :refer [context-menu]])) ;; --- Refs -(def collections-iref - (-> (l/key :colors-collections) +(def project-ref + (-> (l/key :workspace-project) + (l/derive st/state))) + +(def libraries-ref + (-> (comp (l/key :library) (l/key :palettes)) + (l/derive st/state))) + +(defn selected-items-ref [library-id] + (-> (comp (l/key :library-items) (l/key :palettes) (l/key library-id)) (l/derive st/state))) ;; --- Components @@ -30,8 +40,6 @@ (mf/defc palette-item [{:keys [color] :as props}] (let [rgb-vec (hex->rgb color) - rgb-color (apply str "" (interpose ", " rgb-vec)) - select-color (fn [event] (if (kbd/shift? event) @@ -41,93 +49,85 @@ [:div.color-cell {:key (str color) :on-click select-color} [:span.color {:style {:background color}}] - [:span.color-text color] - #_[:span.color-text rgb-color]])) + [:span.color-text color]])) (mf/defc palette - [{:keys [colls left-sidebar?] :as props}] - (let [local (mf/use-state {}) - colls (->> colls - (filter :id) - (sort-by :name)) + [{:keys [libraries left-sidebar?] :as props}] - coll (or (:selected @local) - (first colls)) + (when (and libraries (-> libraries count (> 0))) + (let [state (mf/use-state {:show-menu false + :selected-library (-> libraries first :id)})] + (mf/use-effect (mf/deps (:selected-library @state)) + #(st/emit! (dlib/retrieve-library-data :palettes (:selected-library @state)))) + + (let [items (-> (:selected-library @state) selected-items-ref mf/deref) + doc-width (.. js/document -documentElement -clientWidth) + width (:width @state (* doc-width 0.84)) + offset (:offset @state 0) + visible (/ width 86) + invisible (- (count items) visible) + close #(st/emit! (udw/toggle-layout-flag :colorpalette)) + container (mf/use-ref nil) + container-child (mf/use-ref nil) - doc-width (.. js/document -documentElement -clientWidth) - width (:width @local (* doc-width 0.84)) - offset (:offset @local 0) - visible (/ width 86) - invisible (- (count (:colors coll)) visible) - close #(st/emit! (udw/toggle-layout-flag :colorpalette)) + on-left-arrow-click + (fn [event] + (when (> offset 0) + (let [element (mf/ref-val container-child)] + (swap! state update :offset dec)))) - container (mf/use-ref nil) - container-child (mf/use-ref nil) + on-right-arrow-click + (fn [event] + (when (< offset invisible) + (let [element (mf/ref-val container-child)] + (swap! state update :offset inc)))) - select-coll - (fn [event] - (let [id (read-string (dom/event->value event)) - selected (seek #(= id (:id %)) colls)] - (swap! local assoc :selected selected :position 0))) + on-scroll + (fn [event] + (if (pos? (.. event -nativeEvent -deltaY)) + (on-right-arrow-click event) + (on-left-arrow-click event))) - on-left-arrow-click - (fn [event] - (when (> offset 0) - (let [element (mf/ref-val container-child)] - (swap! local update :offset dec)))) + after-render + (fn [] + (let [dom (mf/ref-val container) + width (.-clientWidth dom)] + (when (not= (:width @state) width) + (swap! state assoc :width width)))) - on-right-arrow-click - (fn [event] - (when (< offset invisible) - (let [element (mf/ref-val container-child)] - (swap! local update :offset inc)))) + handle-click + (fn [library] + (swap! state assoc :selected-library (:id library)))] - on-scroll - (fn [event] - (if (pos? (.. event -nativeEvent -deltaY)) - (on-right-arrow-click event) - (on-left-arrow-click event))) + (mf/use-effect nil after-render) - after-render - (fn [] - (let [dom (mf/ref-val container) - width (.-clientWidth dom)] - (when (not= (:width @local) width) - (swap! local assoc :width width))))] + [:div.color-palette {:class (when left-sidebar? "left-sidebar-open")} + [:& context-menu {:selectable true + :selected (->> libraries (filter #(= (:id %) (:selected-library @state))) first :name) + :show (:show-menu @state) + :on-close #(swap! state assoc :show-menu false) + :options (mapv #(vector (:name %) (partial handle-click %)) libraries)} ] + [:div.color-palette-actions + {:on-click #(swap! state assoc :show-menu true)} + [:div.color-palette-actions-button i/actions]] - (mf/use-effect nil after-render) + [:span.left-arrow {:on-click on-left-arrow-click} i/arrow-slide] - [:div.color-palette {:class (when left-sidebar? "left-sidebar-open")} - [:div.color-palette-actions - [:div.color-palette-actions-button i/actions] - #_[:select.input-select {:on-change select-coll - :default-value (pr-str (:id coll))} - (for [item colls] - [:option {:key (:id item) :value (pr-str (:id item))} - (:name item)])] + [:div.color-palette-content {:ref container :on-wheel on-scroll} + [:div.color-palette-inside {:ref container-child + :style {:position "relative" + :width (str (* 86 (count items)) "px") + :right (str (* 86 offset) "px")}} + (for [item items] + [:& palette-item {:color (:content item) :key (:id item)}])]] - #_[:div.color-palette-buttons - [:div.btn-palette.edit.current i/pencil] - [:div.btn-palette.create i/close]]] - - [:span.left-arrow {:on-click on-left-arrow-click} i/arrow-slide] - - [:div.color-palette-content {:ref container :on-wheel on-scroll} - [:div.color-palette-inside {:ref container-child - :style {:position "relative" - :width (str (* 86 (count (:colors coll))) "px") - :right (str (* 86 offset) "px")}} - #_(for [color (:colors coll)] - [:& palette-item {:color color :key color}]) - (for [color (range 0 20)] - [:& palette-item {:color "#FFFF00" :key color}])]] - - [:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide] - #_[:span.close-palette {:on-click close} i/close]])) + [:span.right-arrow {:on-click on-right-arrow-click} i/arrow-slide]])))) (mf/defc colorpalette [{:keys [left-sidebar?]}] - (let [colls (mf/deref collections-iref)] - #_(mf/use-effect #(st/emit! (udc/fetch-collections))) + (let [team-id (-> project-ref mf/deref :team-id) + libraries (-> libraries-ref mf/deref vals flatten)] + (mf/use-effect #(st/emit! (dlib/retrieve-libraries :palettes))) + (mf/use-effect #(st/emit! (dlib/retrieve-libraries :palettes team-id))) [:& palette {:left-sidebar? left-sidebar? - :colls (vals colls)}])) + :libraries libraries}])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs index a61463c1d6..18111db703 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs @@ -24,7 +24,8 @@ [uxbox.util.uuid :as uuid] [uxbox.util.i18n :as i18n :refer [t]] [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]] - [uxbox.main.data.library :as dlib])) + [uxbox.main.data.library :as dlib] + [uxbox.main.ui.components.context-menu :refer [context-menu]])) (def project-ref (-> (l/key :workspace-project) @@ -34,29 +35,34 @@ (-> (comp (l/key :library) (l/key section)) (l/derive st/state))) -(defn selected-items-ref [library-id] - (-> (comp (l/key :library) (l/key :selected-items) (l/key library-id)) +(defn selected-items-ref [section library-id] + (-> (comp (l/key :library-items) (l/key section) (l/key library-id)) (l/derive st/state))) (mf/defc icons-tab [{:keys [libraries]}] + (when (and libraries (-> libraries count (> 0))) (let [state (mf/use-state {:selected-library (-> libraries first :id)})] + (mf/use-effect (mf/deps libraries) + #(when (not (some (fn [it] (= (:selected-library @state) (-> it :id))) libraries)) + (swap! state assoc :selected-library (-> libraries first :id)))) (mf/use-effect (mf/deps (:selected-library @state)) #(st/emit! (dlib/retrieve-library-data :icons (:selected-library @state)))) [:div.library-tab.icons-tab [:select.input-select.library-tab-libraries - {:on-change #(swap! state assoc :selected-library (-> % dom/get-target dom/get-value))} + {:on-change #(swap! state assoc :selected-library (-> % dom/get-target dom/get-value uuid))} (for [library libraries] [:option.library-tab-libraries-item {:key (:id library) :value (:id library)} (:name library)])] [:div.library-tab-content - (let [items (mf/deref (selected-items-ref (:selected-library @state)))] + (let [items (mf/deref (selected-items-ref :icons (:selected-library @state)))] (for [item items] [:div.library-tab-element - {:key (:id item)} + {:key (:id item) + :on-click #(st/emit! (dw/select-for-drawing :icon item))} [:svg {:view-box (->> item :metadata :view-box (str/join " ")) :width (-> item :metadata :width) :height (-> item :metadat :height) @@ -78,19 +84,30 @@ :value (:id library)} (:name library)])] [:div.library-tab-content - (let [items (mf/deref (selected-items-ref (:selected-library @state)))] + (let [items (mf/deref (selected-items-ref :images (:selected-library @state)))] (for [item items] [:div.library-tab-element - {:key (:id item)} + {:key (:id item) + :on-click #(st/emit! (dw/select-for-drawing :image item))} [:img {:src (:thumb-uri item)}] [:span.library-tab-element-name (:name item)]]))]]))) (mf/defc libraries-toolbox [{:keys [key]}] - (let [team-id (-> project-ref mf/deref :team-id) - locale (i18n/use-locale)] + (let [state (mf/use-state {:menu-open false + :selected :all}) + team-id (-> project-ref mf/deref :team-id) + locale (i18n/use-locale) + key-to-str {:all "All libraries" + :own "My libraries" + :store "Store libraries"} + select-option (fn [option] (swap! state assoc :selected option)) + + filter-libraries (fn [libraries] (case (:selected @state) + :all (-> libraries vals flatten) + :own (libraries team-id) + :store (libraries uuid/zero)))] (mf/use-effect - (mf/deps key) #(do (st/emit! (dlib/retrieve-libraries :icons)) (st/emit! (dlib/retrieve-libraries :images)))) @@ -104,17 +121,27 @@ [:div.libraries-window-bar [:div.libraries-window-bar-title "Libraries"] [:div.libraries-window-bar-options - "All libraries" - [:button {:type "button"} - i/arrow-slide]]] + {:on-click #(swap! state assoc :menu-open true)} + (key-to-str (:selected @state)) + [:button + { + :type "button"} + i/arrow-slide + [:& context-menu {:selectable true + :show (:menu-open @state) + :selected (key-to-str (:selected @state)) + :on-close #(swap! state assoc :menu-open false) + :options (mapv (fn [[key val]] [val #(select-option key)]) key-to-str)}]] + + ]] [:div.tool-window-content [:& tab-container {} [:& tab-element {:id :icons :title "Icons"} - [:& icons-tab {:libraries (-> (libraries-ref :icons) mf/deref vals flatten) }]] + [:& icons-tab {:libraries (-> (libraries-ref :icons) mf/deref filter-libraries) }]] [:& tab-element {:id :images :title "Images"} - [:& images-tab {:libraries (-> (libraries-ref :images) mf/deref vals flatten)}]]]]])) + [:& images-tab {:libraries (-> (libraries-ref :images) mf/deref filter-libraries)}]]]]])) From e4ad75d5d15582a2f4e4eadae39b18895fbbb1d9 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 26 Mar 2020 15:13:46 +0100 Subject: [PATCH 6/7] :violin: moved selections to state so they persists when the component closes --- frontend/src/uxbox/main/data/library.cljs | 21 +++ .../main/ui/components/context_menu.cljs | 1 - .../uxbox/main/ui/workspace/colorpalette.cljs | 23 +-- .../main/ui/workspace/sidebar/libraries.cljs | 153 ++++++++++-------- 4 files changed, 121 insertions(+), 77 deletions(-) diff --git a/frontend/src/uxbox/main/data/library.cljs b/frontend/src/uxbox/main/data/library.cljs index f7596e980d..9c716ee850 100644 --- a/frontend/src/uxbox/main/data/library.cljs +++ b/frontend/src/uxbox/main/data/library.cljs @@ -208,3 +208,24 @@ (filterv #(not (item-ids-set (:id %))) items))] (-> state (update-in [:library-items type library-id] update-fn)))))) + +;; Workspace - select library + +(defn select-library + [type library-id] + (ptk/reify ::select-library + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library-selected type] library-id))))) + + +;; Workspace - change library filter + +(defn change-library-filter + [type filter] + (ptk/reify ::change-library-filter + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library-filter type] filter))))) diff --git a/frontend/src/uxbox/main/ui/components/context_menu.cljs b/frontend/src/uxbox/main/ui/components/context_menu.cljs index 1023ff301c..928baebba7 100644 --- a/frontend/src/uxbox/main/ui/components/context_menu.cljs +++ b/frontend/src/uxbox/main/ui/components/context_menu.cljs @@ -17,7 +17,6 @@ options (gobj/get props "options") is-selectable (gobj/get props "selectable") selected (gobj/get props "selected")] - (println "selected" selected) (when open? [:> dropdown-container props [:div.context-menu {:class (classnames :is-open open? diff --git a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs index 2ba95faa24..cfb8522e28 100644 --- a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs +++ b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs @@ -35,6 +35,10 @@ (-> (comp (l/key :library-items) (l/key :palettes) (l/key library-id)) (l/derive st/state))) +(def selected-library-ref + (-> (comp (l/key :library-selected) (l/key :palettes)) + (l/derive st/state))) + ;; --- Components (mf/defc palette-item @@ -55,12 +59,13 @@ [{:keys [libraries left-sidebar?] :as props}] (when (and libraries (-> libraries count (> 0))) - (let [state (mf/use-state {:show-menu false - :selected-library (-> libraries first :id)})] - (mf/use-effect (mf/deps (:selected-library @state)) - #(st/emit! (dlib/retrieve-library-data :palettes (:selected-library @state)))) + (let [current-selection (or (mf/deref selected-library-ref) (-> libraries first :id)) + state (mf/use-state {:show-menu false })] + (mf/use-effect + (mf/deps current-selection) + #(st/emit! (dlib/retrieve-library-data :palettes current-selection))) - (let [items (-> (:selected-library @state) selected-items-ref mf/deref) + (let [items (-> current-selection selected-items-ref mf/deref) doc-width (.. js/document -documentElement -clientWidth) width (:width @state (* doc-width 0.84)) offset (:offset @state 0) @@ -97,13 +102,13 @@ handle-click (fn [library] - (swap! state assoc :selected-library (:id library)))] + (st/emit! (dlib/select-library :palettes (:id library))))] (mf/use-effect nil after-render) [:div.color-palette {:class (when left-sidebar? "left-sidebar-open")} [:& context-menu {:selectable true - :selected (->> libraries (filter #(= (:id %) (:selected-library @state))) first :name) + :selected (->> libraries (filter #(= (:id %) current-selection)) first :name) :show (:show-menu @state) :on-close #(swap! state assoc :show-menu false) :options (mapv #(vector (:name %) (partial handle-click %)) libraries)} ] @@ -127,7 +132,7 @@ [{:keys [left-sidebar?]}] (let [team-id (-> project-ref mf/deref :team-id) libraries (-> libraries-ref mf/deref vals flatten)] - (mf/use-effect #(st/emit! (dlib/retrieve-libraries :palettes))) - (mf/use-effect #(st/emit! (dlib/retrieve-libraries :palettes team-id))) + (mf/use-effect #(st/emit! (dlib/retrieve-libraries :palettes) + (dlib/retrieve-libraries :palettes team-id))) [:& palette {:left-sidebar? left-sidebar? :libraries libraries}])) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs index 18111db703..fbc0442f18 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs @@ -23,10 +23,13 @@ [uxbox.util.dom :as dom] [uxbox.util.uuid :as uuid] [uxbox.util.i18n :as i18n :refer [t]] + [uxbox.util.data :refer [classnames]] [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]] [uxbox.main.data.library :as dlib] [uxbox.main.ui.components.context-menu :refer [context-menu]])) +;; --- Refs + (def project-ref (-> (l/key :workspace-project) (l/derive st/state))) @@ -39,109 +42,125 @@ (-> (comp (l/key :library-items) (l/key section) (l/key library-id)) (l/derive st/state))) -(mf/defc icons-tab [{:keys [libraries]}] - - (when (and libraries (-> libraries count (> 0))) - (let [state (mf/use-state {:selected-library (-> libraries first :id)})] - (mf/use-effect (mf/deps libraries) - #(when (not (some (fn [it] (= (:selected-library @state) (-> it :id))) libraries)) - (swap! state assoc :selected-library (-> libraries first :id)))) - (mf/use-effect (mf/deps (:selected-library @state)) - #(st/emit! (dlib/retrieve-library-data :icons (:selected-library @state)))) +(defn selected-library-ref [section] + (-> (comp (l/key :library-selected) (l/key section)) + (l/derive st/state))) - [:div.library-tab.icons-tab +(defn selected-filter-ref [section] + (-> (comp (l/key :library-filter) (l/key section)) + (l/derive st/state))) + +;; --- Components + +(mf/defc library-tab [{:keys [libraries section]}] + (when (and libraries (-> libraries count (> 0))) + (let [first-id (-> libraries first :id) + current-selection (or (mf/deref (selected-library-ref section)) first-id)] + + ;; Check if the current selection is in the list of libraries + (mf/use-effect + (mf/deps libraries) + #(when (not (some (fn [it] (= current-selection (-> it :id))) libraries)) + (st/emit! (dlib/select-library section first-id)))) + + ;; Retrieve the library data given the current selected library + (mf/use-effect + (mf/deps current-selection) + #(st/emit! (dlib/retrieve-library-data section current-selection))) + + [:div.library-tab + {:class (classnames :icons-tab (= section :icons) + :images-tab (= section :images))} [:select.input-select.library-tab-libraries - {:on-change #(swap! state assoc :selected-library (-> % dom/get-target dom/get-value uuid))} + {:on-change #(st/emit! (dlib/select-library section (-> % dom/get-target dom/get-value uuid)))} (for [library libraries] [:option.library-tab-libraries-item {:key (:id library) - :value (:id library)} + :value (:id library) + :selected (= current-selection (:id library))} (:name library)])] [:div.library-tab-content - (let [items (mf/deref (selected-items-ref :icons (:selected-library @state)))] + (let [items (mf/deref (selected-items-ref section current-selection))] (for [item items] [:div.library-tab-element {:key (:id item) :on-click #(st/emit! (dw/select-for-drawing :icon item))} - [:svg {:view-box (->> item :metadata :view-box (str/join " ")) - :width (-> item :metadata :width) - :height (-> item :metadat :height) - :dangerouslySetInnerHTML {:__html (:content item)}}] - [:span.library-tab-element-name (:name item)]]))]]))) + (if (= section :icons) + [:* ;; ICONS + [:svg {:view-box (->> item :metadata :view-box (str/join " ")) + :width (-> item :metadata :width) + :height (-> item :metadat :height) + :dangerouslySetInnerHTML {:__html (:content item)}}] + [:span.library-tab-element-name (:name item)]] -(mf/defc images-tab [{:keys [libraries]}] - (when (and libraries (-> libraries count (> 0))) - (let [state (mf/use-state {:selected-library (-> libraries first :id)})] - (mf/use-effect (mf/deps (:selected-library @state)) - #(st/emit! (dlib/retrieve-library-data :images (:selected-library @state)))) - - [:div.library-tab.images-tab - [:select.input-select.library-tab-libraries - {:on-change #(swap! state assoc :selected-library (-> % dom/get-target dom/get-value))} - (for [library libraries] - [:option.library-tab-libraries-item - {:key (:id library) - :value (:id library)} - (:name library)])] - [:div.library-tab-content - (let [items (mf/deref (selected-items-ref :images (:selected-library @state)))] - (for [item items] - [:div.library-tab-element - {:key (:id item) - :on-click #(st/emit! (dw/select-for-drawing :image item))} - [:img {:src (:thumb-uri item)}] - [:span.library-tab-element-name (:name item)]]))]]))) + [:* ;; IMAGES + [:img {:src (:thumb-uri item)}] + [:span.library-tab-element-name (:name item)]])]))]]))) (mf/defc libraries-toolbox [{:keys [key]}] - (let [state (mf/use-state {:menu-open false - :selected :all}) + (let [state (mf/use-state {:menu-open false}) + selected-filter (fn [section] (or (mf/deref (selected-filter-ref section)) :all)) team-id (-> project-ref mf/deref :team-id) locale (i18n/use-locale) - key-to-str {:all "All libraries" - :own "My libraries" - :store "Store libraries"} - select-option (fn [option] (swap! state assoc :selected option)) - filter-libraries (fn [libraries] (case (:selected @state) - :all (-> libraries vals flatten) - :own (libraries team-id) - :store (libraries uuid/zero)))] - (mf/use-effect - #(do - (st/emit! (dlib/retrieve-libraries :icons)) - (st/emit! (dlib/retrieve-libraries :images)))) + filter-to-str {:all "All libraries" + :own "My libraries" + :store "Store libraries"} + + select-option + (fn [option] + (st/emit! + (dlib/change-library-filter :icons option) + (dlib/change-library-filter :images option))) + + filter-libraries + (fn [section libraries] + (case (selected-filter section) + :all (-> libraries vals flatten) + :own (libraries team-id) + :store (libraries uuid/zero))) + + get-libraries + (fn [section] (->> (libraries-ref section) + mf/deref + (filter-libraries section)))] + (mf/use-effect (mf/deps team-id) #(when team-id - (do - (st/emit! (dlib/retrieve-libraries :icons team-id)) - (st/emit! (dlib/retrieve-libraries :images team-id))))) + (st/emit! (dlib/retrieve-libraries :icons) + (dlib/retrieve-libraries :images) + (dlib/retrieve-libraries :icons team-id) + (dlib/retrieve-libraries :images team-id)))) + [:div#libraries.tool-window [:div.libraries-window-bar [:div.libraries-window-bar-title "Libraries"] [:div.libraries-window-bar-options {:on-click #(swap! state assoc :menu-open true)} - (key-to-str (:selected @state)) + (filter-to-str (selected-filter :icons)) [:button { :type "button"} i/arrow-slide - [:& context-menu {:selectable true - :show (:menu-open @state) - :selected (key-to-str (:selected @state)) - :on-close #(swap! state assoc :menu-open false) - :options (mapv (fn [[key val]] [val #(select-option key)]) key-to-str)}]] - - ]] + [:& context-menu + {:selectable true + :show (:menu-open @state) + :selected (filter-to-str (selected-filter :icons)) + :on-close #(swap! state assoc :menu-open false) + :options (mapv (fn [[key val]] [val #(select-option key)]) filter-to-str)}]]]] + [:div.tool-window-content [:& tab-container {} [:& tab-element {:id :icons :title "Icons"} - [:& icons-tab {:libraries (-> (libraries-ref :icons) mf/deref filter-libraries) }]] + [:& library-tab {:section :icons + :libraries (get-libraries :icons) }]] [:& tab-element {:id :images :title "Images"} - [:& images-tab {:libraries (-> (libraries-ref :images) mf/deref filter-libraries)}]]]]])) + [:& library-tab {:section :images + :libraries (get-libraries :images)}]]]]])) From 15c5c41a74a5034535bbd8d7f6a23e363b8bc674 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 26 Mar 2020 15:24:48 +0100 Subject: [PATCH 7/7] :uk: fixed locales --- frontend/resources/locales.json | 213 ++++++++++++------ .../main/ui/workspace/sidebar/libraries.cljs | 15 +- 2 files changed, 156 insertions(+), 72 deletions(-) diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index 155778fed6..3f9f493395 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -1,18 +1,18 @@ { "dashboard.grid.delete" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:73", "src/uxbox/main/ui/dashboard/grid.cljs:91" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:72", "src/uxbox/main/ui/dashboard/grid.cljs:92" ], "translations" : { "en" : "Delete" } }, "dashboard.grid.edit" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:72", "src/uxbox/main/ui/dashboard/grid.cljs:90" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:71", "src/uxbox/main/ui/dashboard/grid.cljs:91" ], "translations" : { "en" : "Edit" } }, "dashboard.grid.empty-files" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:113" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:114" ], "translations" : { "en" : "You still have no files here" } @@ -25,7 +25,7 @@ "unused" : true }, "dashboard.header.draft" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:58" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:57" ], "translations" : { "en" : "Draft" } @@ -45,40 +45,40 @@ "unused" : true }, "dashboard.header.new-file" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:78" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:77" ], "translations" : { "en" : "+ New file" } }, "dashboard.header.new-project" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/recent_files.cljs:54" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/recent_files.cljs:53" ], "translations" : { "en" : "+ New project" } }, "dashboard.header.profile-menu.logout" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/profile.cljs:59", "src/uxbox/main/ui/workspace/header.cljs:93" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/profile.cljs:59" ], "translations" : { "en" : "Exit", "fr" : "Quitter" } }, "dashboard.header.profile-menu.password" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/profile.cljs:56", "src/uxbox/main/ui/workspace/header.cljs:92" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/profile.cljs:56" ], "translations" : { "en" : "Password", "fr" : "Mot de passe" } }, "dashboard.header.profile-menu.profile" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/profile.cljs:53", "src/uxbox/main/ui/workspace/header.cljs:91" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/profile.cljs:53" ], "translations" : { "en" : "Profile", "fr" : "Profil" } }, "dashboard.header.project" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:74" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/project.cljs:73" ], "translations" : { "en" : "Project %s" } @@ -127,25 +127,25 @@ "unused" : true }, "dashboard.library.menu.icons" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:103" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:96" ], "translations" : { "en" : "Icons" } }, "dashboard.library.menu.images" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:107" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:100" ], "translations" : { "en" : "Images" } }, "dashboard.library.menu.palettes" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:111" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:104" ], "translations" : { "en" : "Palettes" } }, "dashboard.search.no-matches-for" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/search.cljs:47" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/search.cljs:48" ], "translations" : { "en" : "No matches found for \"%s\"" } @@ -157,13 +157,13 @@ "unused" : true }, "dashboard.search.searching-for" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/search.cljs:43" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/search.cljs:44" ], "translations" : { "en" : "Searching for %s..." } }, "dashboard.search.type-something" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/search.cljs:39" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/search.cljs:40" ], "translations" : { "en" : "Type to search results" } @@ -200,19 +200,19 @@ } }, "ds.button.delete" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:147", "src/uxbox/main/ui/dashboard/library.cljs:192", "src/uxbox/main/ui/dashboard/library.cljs:216", "src/uxbox/main/ui/dashboard/library.cljs:243" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:152", "src/uxbox/main/ui/dashboard/library.cljs:220", "src/uxbox/main/ui/dashboard/library.cljs:257", "src/uxbox/main/ui/dashboard/library.cljs:296" ], "translations" : { "en" : "Delete" } }, "ds.button.rename" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:146" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:149" ], "translations" : { "en" : "Rename" } }, "ds.button.save" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:51" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:54" ], "translations" : { "en" : "Save" } @@ -253,21 +253,21 @@ "unused" : true }, "ds.confirm-cancel" : { - "used-in" : [ "src/uxbox/main/ui/confirm.cljs:38" ], + "used-in" : [ "src/uxbox/main/ui/confirm.cljs:19" ], "translations" : { "en" : "Cancel", "fr" : "Annuler" } }, "ds.confirm-ok" : { - "used-in" : [ "src/uxbox/main/ui/confirm.cljs:34" ], + "used-in" : [ "src/uxbox/main/ui/confirm.cljs:20" ], "translations" : { "en" : "Ok", "fr" : "Ok" } }, "ds.confirm-title" : { - "used-in" : [ "src/uxbox/main/ui/confirm.cljs:28" ], + "used-in" : [ "src/uxbox/main/ui/confirm.cljs:18" ], "translations" : { "en" : "Are you sure?", "fr" : "Êtes-vous sûr ?" @@ -380,7 +380,7 @@ "unused" : true }, "ds.new-file" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:109", "src/uxbox/main/ui/dashboard/grid.cljs:115" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/grid.cljs:110", "src/uxbox/main/ui/dashboard/grid.cljs:116" ], "translations" : { "en" : "+ New File", "fr" : null @@ -450,7 +450,7 @@ "unused" : true }, "ds.store-images-title" : { - "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:181" ], + "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:180" ], "translations" : { "en" : "IMAGES STORE", "fr" : "BOUTIQUE" @@ -499,7 +499,7 @@ "unused" : true }, "ds.your-images-title" : { - "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:178" ], + "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:177" ], "translations" : { "en" : "YOUR IMAGES", "fr" : "VOS IMAGES" @@ -534,21 +534,21 @@ } }, "errors.generic" : { - "used-in" : [ "src/uxbox/main/ui.cljs:160" ], + "used-in" : [ "src/uxbox/main/ui.cljs:162" ], "translations" : { "en" : "Something wrong has happened.", "fr" : "Quelque chose c'est mal passé." } }, "errors.network" : { - "used-in" : [ "src/uxbox/main/ui.cljs:154" ], + "used-in" : [ "src/uxbox/main/ui.cljs:156" ], "translations" : { "en" : "Unable to connect to backend server.", "fr" : "Impossible de se connecter au serveur principal." } }, "header.sitemap" : { - "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:96" ], + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:154" ], "translations" : { "en" : null, "fr" : null @@ -562,28 +562,28 @@ } }, "image.import-library" : { - "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:170" ], + "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:169" ], "translations" : { "en" : "Import image from library", "fr" : "Importer une image depuis une librairie" } }, "image.new" : { - "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:84" ], + "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:83" ], "translations" : { "en" : "New image", "fr" : "Nouvelle image" } }, "image.select" : { - "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:90", "src/uxbox/main/ui/workspace/images.cljs:95" ], + "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:89", "src/uxbox/main/ui/workspace/images.cljs:94" ], "translations" : { "en" : "Select from library", "fr" : "Choisir depuis une librairie" } }, "image.upload" : { - "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:102" ], + "used-in" : [ "src/uxbox/main/ui/workspace/images.cljs:101" ], "translations" : { "en" : "Upload file", "fr" : "Envoyer un fichier" @@ -645,7 +645,7 @@ "unused" : true }, "modal.create-color.new-color" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:45" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:48" ], "translations" : { "en" : "New Color" } @@ -658,7 +658,7 @@ } }, "profile.recovery.go-to-login" : { - "used-in" : [ "src/uxbox/main/ui/profile/recovery_request.cljs:65", "src/uxbox/main/ui/profile/recovery.cljs:81" ], + "used-in" : [ "src/uxbox/main/ui/profile/recovery.cljs:81", "src/uxbox/main/ui/profile/recovery_request.cljs:65" ], "translations" : { "en" : "Go back!", "fr" : "Retour!" @@ -798,23 +798,12 @@ } }, "settings.password" : { - "used-in" : [ "src/uxbox/main/ui/settings/header.cljs:37" ], + "used-in" : [ "src/uxbox/main/ui/settings/header.cljs:34" ], "translations" : { "en" : "PASSWORD", "fr" : "MOT DE PASSE" } }, - "workspace.header.menu.hide-rules": "Hide rules", - "workspace.header.menu.show-rules": "Show rules", - "workspace.header.menu.hide-grid": "Hide grid", - "workspace.header.menu.show-grid": "Show grid", - "workspace.header.menu.hide-layers": "Hide layers", - "workspace.header.menu.show-layers": "Show layers", - "workspace.header.menu.hide-palette": "Hide color palette", - "workspace.header.menu.show-palette": "Show color palette", - "workspace.header.menu.hide-libraries": "Hide libraries", - "workspace.header.menu.show-libraries": "Show libraries", - "settings.password.change-password" : { "used-in" : [ "src/uxbox/main/ui/settings/password.cljs:64" ], "translations" : { @@ -851,14 +840,14 @@ } }, "settings.profile" : { - "used-in" : [ "src/uxbox/main/ui/settings/header.cljs:34" ], + "used-in" : [ "src/uxbox/main/ui/settings/header.cljs:30" ], "translations" : { "en" : "PROFILE", "fr" : "PROFIL" } }, "settings.profile.lang" : { - "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:91" ], + "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:92" ], "translations" : { "en" : "Default language", "fr" : "Langue par défaut" @@ -872,28 +861,28 @@ } }, "settings.profile.section-basic-data" : { - "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:64" ], + "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:66" ], "translations" : { "en" : "Name, username and email", "fr" : "Nom, nom d'utilisateur et adresse email" } }, "settings.profile.your-avatar" : { - "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:135" ], + "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:138" ], "translations" : { "en" : "Your avatar", "fr" : "Votre avatar" } }, "settings.profile.your-email" : { - "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:85" ], + "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:87" ], "translations" : { "en" : null, "fr" : null } }, "settings.profile.your-name" : { - "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:72" ], + "used-in" : [ "src/uxbox/main/ui/settings/profile.cljs:75" ], "translations" : { "en" : "Your name", "fr" : "Votre nom complet" @@ -907,7 +896,7 @@ "unused" : true }, "settings.update-settings" : { - "used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:42", "src/uxbox/main/ui/settings/password.cljs:102", "src/uxbox/main/ui/settings/profile.cljs:104" ], + "used-in" : [ "src/uxbox/main/ui/settings/notifications.cljs:42", "src/uxbox/main/ui/settings/password.cljs:102", "src/uxbox/main/ui/settings/profile.cljs:105" ], "translations" : { "en" : "Update settings", "fr" : "Mettre à jour les paramètres" @@ -990,6 +979,66 @@ }, "unused" : true }, + "workspace.header.menu.hide-grid" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:104" ], + "translations" : { + "en" : "Hide grid" + } + }, + "workspace.header.menu.hide-layers" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:111" ], + "translations" : { + "en" : "Hide layers" + } + }, + "workspace.header.menu.hide-libraries" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:125" ], + "translations" : { + "en" : "Hide libraries" + } + }, + "workspace.header.menu.hide-palette" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:118" ], + "translations" : { + "en" : "Hide color palette" + } + }, + "workspace.header.menu.hide-rules" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:97" ], + "translations" : { + "en" : "Hide rules" + } + }, + "workspace.header.menu.show-grid" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:105" ], + "translations" : { + "en" : "Show grid" + } + }, + "workspace.header.menu.show-layers" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:112" ], + "translations" : { + "en" : "Show layers" + } + }, + "workspace.header.menu.show-libraries" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:126" ], + "translations" : { + "en" : "Show libraries" + } + }, + "workspace.header.menu.show-palette" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:119" ], + "translations" : { + "en" : "Show color palette" + } + }, + "workspace.header.menu.show-rules" : { + "used-in" : [ "src/uxbox/main/ui/workspace/header.cljs:98" ], + "translations" : { + "en" : "Show rules" + } + }, "workspace.header.path" : { "translations" : { "en" : "Path", @@ -1025,29 +1074,65 @@ }, "unused" : true }, + "workspace.library.all" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/libraries.cljs:106" ], + "translations" : { + "en" : "All libraries" + } + }, + "workspace.library.icons" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/libraries.cljs:156" ], + "translations" : { + "en" : "Icons" + } + }, + "workspace.library.images" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/libraries.cljs:161" ], + "translations" : { + "en" : "Images" + } + }, + "workspace.library.libraries" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/libraries.cljs:138" ], + "translations" : { + "en" : "Libraries" + } + }, + "workspace.library.own" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/libraries.cljs:107" ], + "translations" : { + "en" : "My libraries" + } + }, + "workspace.library.store" : { + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/libraries.cljs:108" ], + "translations" : { + "en" : "Store libraries" + } + }, "workspace.options.color" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:124", "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:47", "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:81" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/fill.cljs:47", "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:124", "src/uxbox/main/ui/workspace/sidebar/options/stroke.cljs:81" ], "translations" : { "en" : "Color", "fr" : "Couleur" } }, "workspace.options.font-family" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:203" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:202" ], "translations" : { "en" : "Font Family", "fr" : "Police de caractères" } }, "workspace.options.font-options" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:201" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:200" ], "translations" : { "en" : "Fonts & Font Size", "fr" : "TODO" } }, "workspace.options.font-weight" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:212" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:211" ], "translations" : { "en" : "Font Size & Weight", "fr" : "Taille et graisse" @@ -1061,14 +1146,14 @@ } }, "workspace.options.line-height-letter-spacing" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:244" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:243" ], "translations" : { "en" : "Line height and Letter spacing", "fr" : "Hauteur de ligne et Espacement de caractères" } }, "workspace.options.measures" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:69", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:66", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:62", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:64", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:55", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:66" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:64", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:55", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:66", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:62", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:66", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:68" ], "translations" : { "en" : "Size, position & rotation", "fr" : "Taille, position et rotation" @@ -1082,21 +1167,21 @@ } }, "workspace.options.position" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:98", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:95", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:91", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:92", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:84", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:95" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:92", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:84", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:95", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:91", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:95", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:97" ], "translations" : { "en" : "Position", "fr" : "Position" } }, "workspace.options.rotation-radius" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:115", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:112", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:108", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:107", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:112" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:107", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:112", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:108", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:112", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:114" ], "translations" : { "en" : "Rotation & Radius", "fr" : "TODO" } }, "workspace.options.size" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:114", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:71", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:68", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:64", "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:68", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:57", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:68" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/circle.cljs:68", "src/uxbox/main/ui/workspace/sidebar/options/frame.cljs:57", "src/uxbox/main/ui/workspace/sidebar/options/icon.cljs:68", "src/uxbox/main/ui/workspace/sidebar/options/image.cljs:64", "src/uxbox/main/ui/workspace/sidebar/options/page.cljs:114", "src/uxbox/main/ui/workspace/sidebar/options/rect.cljs:68", "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:70" ], "translations" : { "en" : "Size", "fr" : "Taille" @@ -1152,7 +1237,7 @@ } }, "workspace.options.text-align" : { - "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:263" ], + "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/options/text.cljs:262" ], "translations" : { "en" : "Text Alignment", "fr" : "Alignement de texte" @@ -1180,7 +1265,7 @@ } }, "workspace.viewport.click-to-close-path" : { - "used-in" : [ "src/uxbox/main/ui/workspace/drawarea.cljs:334" ], + "used-in" : [ "src/uxbox/main/ui/workspace/drawarea.cljs:335" ], "translations" : { "en" : "Click to close the path" } diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs index fbc0442f18..b84c3dfdb4 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/libraries.cljs @@ -22,7 +22,7 @@ [uxbox.main.ui.workspace.sortable :refer [use-sortable]] [uxbox.util.dom :as dom] [uxbox.util.uuid :as uuid] - [uxbox.util.i18n :as i18n :refer [t]] + [uxbox.util.i18n :as i18n :refer [tr]] [uxbox.util.data :refer [classnames]] [uxbox.main.ui.components.tab-container :refer [tab-container tab-element]] [uxbox.main.data.library :as dlib] @@ -102,11 +102,10 @@ (let [state (mf/use-state {:menu-open false}) selected-filter (fn [section] (or (mf/deref (selected-filter-ref section)) :all)) team-id (-> project-ref mf/deref :team-id) - locale (i18n/use-locale) - filter-to-str {:all "All libraries" - :own "My libraries" - :store "Store libraries"} + filter-to-str {:all (tr "workspace.library.all") + :own (tr "workspace.library.own") + :store (tr "workspace.library.store")} select-option (fn [option] @@ -136,7 +135,7 @@ [:div#libraries.tool-window [:div.libraries-window-bar - [:div.libraries-window-bar-title "Libraries"] + [:div.libraries-window-bar-title (tr "workspace.library.libraries")] [:div.libraries-window-bar-options {:on-click #(swap! state assoc :menu-open true)} (filter-to-str (selected-filter :icons)) @@ -154,12 +153,12 @@ [:div.tool-window-content [:& tab-container {} [:& tab-element - {:id :icons :title "Icons"} + {:id :icons :title (tr "workspace.library.icons")} [:& library-tab {:section :icons :libraries (get-libraries :icons) }]] [:& tab-element - {:id :images :title "Images"} + {:id :images :title (tr "workspace.library.images")} [:& library-tab {:section :images :libraries (get-libraries :images)}]]]]]))