From e700f937db49206c3b8e6c258619d406011838cf Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 Mar 2020 13:12:02 +0100 Subject: [PATCH 1/3] :japanese_ogre: layout for library --- .../styles/common/dependencies/colors.scss | 2 + frontend/resources/styles/main.scss | 2 + .../styles/main/layouts/library-page.scss | 273 ++++++++++++++++++ .../styles/main/partials/context-menu.scss | 34 +++ .../styles/main/partials/library-bar.scss | 1 + frontend/src/uxbox/builtins/icons.cljs | 1 + frontend/src/uxbox/main/ui.cljs | 3 +- frontend/src/uxbox/main/ui/dashboard.cljs | 6 +- .../ui/dashboard/components/context_menu.cljs | 15 + .../src/uxbox/main/ui/dashboard/elements.cljs | 2 +- .../src/uxbox/main/ui/dashboard/header.cljs | 26 -- .../src/uxbox/main/ui/dashboard/library.cljs | 136 +++++++++ .../uxbox/main/ui/dashboard/recent_files.cljs | 25 +- .../src/uxbox/main/ui/dashboard/sidebar.cljs | 5 +- 14 files changed, 492 insertions(+), 39 deletions(-) create mode 100644 frontend/resources/styles/main/layouts/library-page.scss create mode 100644 frontend/resources/styles/main/partials/context-menu.scss create mode 100644 frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs delete mode 100644 frontend/src/uxbox/main/ui/dashboard/header.cljs create mode 100644 frontend/src/uxbox/main/ui/dashboard/library.cljs diff --git a/frontend/resources/styles/common/dependencies/colors.scss b/frontend/resources/styles/common/dependencies/colors.scss index b896180efa..8c92847b78 100644 --- a/frontend/resources/styles/common/dependencies/colors.scss +++ b/frontend/resources/styles/common/dependencies/colors.scss @@ -26,10 +26,12 @@ $mix-percentage-dark: 81%; $mix-percentage-darker: 60%; $mix-percentage-light: 80%; $mix-percentage-lighter: 20%; +$mix-percentage-lightest: 10%; // Gray scale $color-gray-light: mix($color-gray, $color-white, $mix-percentage-light); $color-gray-lighter: mix($color-gray, $color-white, $mix-percentage-lighter); +$color-gray-lightest: mix($color-gray, $color-white, $mix-percentage-lightest); $color-gray-dark: mix($color-gray, $color-black, $mix-percentage-dark); $color-gray-darker: mix($color-gray, $color-black, $mix-percentage-darker); $color-gray-10: #E3E3E3; diff --git a/frontend/resources/styles/main.scss b/frontend/resources/styles/main.scss index 1106d6b6b4..dd77f3c8a2 100644 --- a/frontend/resources/styles/main.scss +++ b/frontend/resources/styles/main.scss @@ -29,6 +29,7 @@ @import 'main/layouts/login'; @import 'main/layouts/projects-page'; @import 'main/layouts/recent-files-page'; +@import 'main/layouts/library-page'; //################################################# // Commons @@ -63,6 +64,7 @@ @import 'main/partials/colorpicker'; @import 'main/partials/forms'; @import 'main/partials/loader'; +@import 'main/partials/context-menu'; //################################################# // Resources diff --git a/frontend/resources/styles/main/layouts/library-page.scss b/frontend/resources/styles/main/layouts/library-page.scss new file mode 100644 index 0000000000..2b169471b9 --- /dev/null +++ b/frontend/resources/styles/main/layouts/library-page.scss @@ -0,0 +1,273 @@ +.library-page { + height: 100%; + display: grid; + grid-template-rows: 40px 1fr; + grid-template-columns: 14.5rem 1fr; + grid-template-areas: "header header" "sidebar content"; + border-right: 1px solid $color-gray; + + & .main-bar { + grid-area: header; + } + + & .library-sidebar { + grid-area: sidebar; + } + + & .library-content { + grid-area: content; + overflow: hidden; + } +} + +.library-page #main-bar { + position: relative; +} + +.library-header-navigation { + display: flex; + position: absolute; + left: 0; + width: 100%; + justify-content: center; +} + +.library-header-navigation-item { + margin: 0 $size-4; + color: $color-gray; + text-transform: uppercase; + border-bottom: 1px solid transparent; + + &:hover, &.current { + color: $color-black; + border-bottom: 1px solid $color-primary; + } +} + +.library-sidebar { + background-color: $color-white; + padding: $size-2; +} + +.library-sidebar-add-item { + background-color: $color-primary; + border-radius: 2px; + border: none; + cursor: pointer; + font-size: 12px; + padding: $size-2; + width: 100%; + + &:hover { + background-color: $color-black; + color: $color-primary; + } +} + +.library-sidebar-list { + margin-top: $size-4; +} + +.library-sidebar-list-element { + padding: $size-4 $size-2; + cursor: pointer; + + & a { + color: $color-black; + } + + &:hover { + background-color: $color-primary-lighter; + } + + &.current a { + font-weight: bold; + } +} + +.library-top-menu { + width: 100%; + display: flex; + flex-direction: row; + border-bottom: 1px solid #e3e3e3; + justify-content: space-between; + padding: $size-2 1.5rem; + + svg { + width: 16px; + height: 16px; + fill: #7C7C7C; + cursor: pointer; + + &:hover { + fill: $color-primary; + } + } + + & > * { + align-items: center; + } +} + +.library-top-menu-current-element { + display: flex; +} + +.library-top-menu-current-element-name { + font-size: 15px; + line-height: 18px; + color: $color-black; + font-weight: normal; + margin-right: $size-2; +} + +.library-top-menu-current-action { + & svg { + width: 10px; + height: 10px; + } +} + +.library-top-menu-actions { + display: flex; + + .btn-dashboard { + background-color: transparent; + margin-left: $size-2; + } +} + +.library-page-cards-container { + padding: $size-2; + display: flex; + flex-wrap: wrap; + overflow: scroll; + height: 100%; + align-content: flex-start; +} + +.library-card { + margin: $size-2; + background: $color-white; + + &.library-icon, &.library-color { + width: 200px; + } + + &.library-image { + width: 300px; + } + + height: 200px; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); + border-radius: 4px; + position: relative; + + & .library-card-footer-menu { + visibility: hidden; + opacity: 0; + } + + &:hover .library-card-footer-menu { + visibility: visible; + opacity: 1; + } +} + +.library-card .input-checkbox { + margin: 0; + top: $size-2; + right: $size-2; + position: absolute; + + & label { + margin: 0; + + &:before { + margin: 0; + background-color: white; + border: 1px solid #AFB2BF; + } + } +} + + +.library-card-image { + height: 136px; + + .library-color & { + height: 104px; + } + + padding: $size-2; + overflow: hidden; + text-align: center; + display: flex; + + & svg, & img { + margin: auto; + width: auto; + height: auto; + max-height: 100%; + max-width: 100%; + } +} + +.library-card-footer { + border-top: 1px solid $color-gray-lighter; + padding: $size-2 $size-2 $size-2 $size-4; + display: grid; + grid-template-rows: 50% 50%; + grid-template-columns: 1fr 16px 1px; + grid-template-areas: "name . ." "timestamp menu options"; + + .library-card.library-color & { + height: 50%; + text-align: center; + } +} + +.library-card-footer-name { + color: $color-black; + grid-area: name; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.library-card-footer-timestamp { + font-size: 12px; + grid-area: timestamp; +} + +.library-card-footer-color { + font-size: 15px; + grid-area: timestamp; +} + +.library-card-footer-color-label { + color: $color-gray; + margin-right: 0.25rem; +} + +.library-card-footer-color-rgb { + color: $color-black; +} + +.library-card-footer-menu { + grid-area: menu; + cursor: pointer; + + & svg { + width: 16px; + height: 16px; + } +} + + +.library-card-footer .context-menu { + grid-area: options; + & .context-menu-items { + top: 1.5rem; + } +} diff --git a/frontend/resources/styles/main/partials/context-menu.scss b/frontend/resources/styles/main/partials/context-menu.scss new file mode 100644 index 0000000000..2a97cb07d0 --- /dev/null +++ b/frontend/resources/styles/main/partials/context-menu.scss @@ -0,0 +1,34 @@ +.context-menu { + position: relative; + visibility: hidden; + opacity: 0; + z-index: 100; +} + +.context-menu.is-open { + position: relative; + display: block; + opacity: 1; + visibility: visible; +} + +.context-menu-items { + background: $color-white; + box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); + left: -$size-4; + min-width: 7rem; + position: absolute; + top: $size-3; +} + +.context-menu-action { + color: $color-black; + display: block; + font-size: 12px; + padding: $size-2 $size-4; + + &:hover { + color: $color-black; + background: $color-gray-lightest; + } +} diff --git a/frontend/resources/styles/main/partials/library-bar.scss b/frontend/resources/styles/main/partials/library-bar.scss index d97b08c047..ec1449ace9 100644 --- a/frontend/resources/styles/main/partials/library-bar.scss +++ b/frontend/resources/styles/main/partials/library-bar.scss @@ -9,6 +9,7 @@ background-color: $color-white; .library-bar-inside { + border-right: 1px solid $color-gray-lighter; display: flex; flex-direction: column; height: 100%; diff --git a/frontend/src/uxbox/builtins/icons.cljs b/frontend/src/uxbox/builtins/icons.cljs index b924ca0c54..8c92eb9056 100644 --- a/frontend/src/uxbox/builtins/icons.cljs +++ b/frontend/src/uxbox/builtins/icons.cljs @@ -17,6 +17,7 @@ (def align-right (icon-xref :align-right)) (def alignment (icon-xref :alignment)) (def arrow (icon-xref :arrow)) +(def arrow-down (icon-xref :arrow-down)) (def arrow-end (icon-xref :arrow-end)) (def arrow-slide (icon-xref :arrow-slide)) (def artboard (icon-xref :artboard)) diff --git a/frontend/src/uxbox/main/ui.cljs b/frontend/src/uxbox/main/ui.cljs index 637727b948..bdc69b7173 100644 --- a/frontend/src/uxbox/main/ui.cljs +++ b/frontend/src/uxbox/main/ui.cljs @@ -90,7 +90,8 @@ (:dashboard-search :dashboard-team - :dashboard-project) + :dashboard-project + :dashboard-library) (mf/element dashboard #js {:route route}) :workspace diff --git a/frontend/src/uxbox/main/ui/dashboard.cljs b/frontend/src/uxbox/main/ui/dashboard.cljs index 6e56b153ed..90a3b02b2d 100644 --- a/frontend/src/uxbox/main/ui/dashboard.cljs +++ b/frontend/src/uxbox/main/ui/dashboard.cljs @@ -16,11 +16,11 @@ [uxbox.common.exceptions :as ex] [uxbox.common.spec :as us] [uxbox.main.refs :as refs] - [uxbox.main.ui.dashboard.header :refer [header]] [uxbox.main.ui.dashboard.sidebar :refer [sidebar]] [uxbox.main.ui.dashboard.search :refer [search-page]] [uxbox.main.ui.dashboard.project :refer [project-page]] [uxbox.main.ui.dashboard.recent-files :refer [recent-files-page]] + [uxbox.main.ui.dashboard.library :refer [library-page]] [uxbox.main.ui.dashboard.profile :refer [profile-section]] [uxbox.main.ui.messages :refer [messages-widget]])) @@ -66,7 +66,6 @@ :search-term search-term :section section}] [:div.dashboard-content - [:& header] (case section :dashboard-search (mf/element search-page #js {:team-id team-id :search-term search-term}) @@ -74,6 +73,9 @@ :dashboard-team (mf/element recent-files-page #js {:team-id team-id}) + :dashboard-library + (mf/element library-page #js {:team-id team-id}) + :dashboard-project (mf/element project-page #js {:team-id team-id :project-id project-id}))]]]) diff --git a/frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs b/frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs new file mode 100644 index 0000000000..dd55fb276d --- /dev/null +++ b/frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs @@ -0,0 +1,15 @@ +(ns uxbox.main.ui.dashboard.components.context-menu + (:require + [rumext.alpha :as mf] + [uxbox.util.uuid :as uuid])) + +(mf/defc context-menu + [{ :keys [ is-open options ]}] + [:div.context-menu + { :class-name (when is-open "is-open")} + [:ul.context-menu-items + (for [[action-name action-handler] options] + [:li.context-menu-item + { :key (uuid/next)} + [:a.context-menu-action {:on-click action-handler} action-name]])]]) + diff --git a/frontend/src/uxbox/main/ui/dashboard/elements.cljs b/frontend/src/uxbox/main/ui/dashboard/elements.cljs index 2c87e6b24f..842ad0b07b 100644 --- a/frontend/src/uxbox/main/ui/dashboard/elements.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/elements.cljs @@ -12,7 +12,7 @@ [uxbox.builtins.icons :as i] [rumext.core :as mx :include-macros true] [uxbox.main.ui.lightbox :as lbx] - [uxbox.main.ui.dashboard.header :refer (header)] + #_[uxbox.main.ui.dashboard.header :refer (header)] [uxbox.util.dom :as dom])) ;; --- Page Title diff --git a/frontend/src/uxbox/main/ui/dashboard/header.cljs b/frontend/src/uxbox/main/ui/dashboard/header.cljs deleted file mode 100644 index 9a56e150f7..0000000000 --- a/frontend/src/uxbox/main/ui/dashboard/header.cljs +++ /dev/null @@ -1,26 +0,0 @@ -;; This Source Code Form is subject to the terms of the Mozilla Public -;; License, v. 2.0. If a copy of the MPL was not distributed with this -;; file, You can obtain one at http://mozilla.org/MPL/2.0/. -;; -;; This Source Code Form is "Incompatible With Secondary Licenses", as -;; defined by the Mozilla Public License, v. 2.0. -;; -;; Copyright (c) 2015-2020 Andrey Antukh -;; Copyright (c) 2015-2020 Juan de la Cruz - -(ns uxbox.main.ui.dashboard.header - (:require - [rumext.alpha :as mf] - [uxbox.util.i18n :as i18n :refer [t]] - [uxbox.main.ui.dashboard.profile :refer [profile-section]])) - - -;; --- Component: Header - -(mf/defc header - [{:keys [profile] :as props}] - (let [locale (i18n/use-locale)] - [:header#main-bar.main-bar - [:h1.dashboard-title "Personal"] - [:a.btn-dashboard "+ New project"]])) - diff --git a/frontend/src/uxbox/main/ui/dashboard/library.cljs b/frontend/src/uxbox/main/ui/dashboard/library.cljs new file mode 100644 index 0000000000..5cad30fec0 --- /dev/null +++ b/frontend/src/uxbox/main/ui/dashboard/library.cljs @@ -0,0 +1,136 @@ +;; 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) 2015-2020 Andrey Antukh +;; Copyright (c) 2015-2020 Juan de la Cruz + +(ns uxbox.main.ui.dashboard.library + (:require + [lentes.core :as l] + [rumext.alpha :as mf] + [uxbox.builtins.icons :as i] + [uxbox.main.store :as st] + [uxbox.main.refs :as refs] + [uxbox.util.i18n :as i18n :refer [t tr]] + [uxbox.main.ui.dashboard.components.context-menu :refer [context-menu]])) + +(mf/defc library-header + [{:keys [profile] :as props}] + (let [locale (i18n/use-locale)] + [:header#main-bar.main-bar + [:h1.dashboard-title "Libraries"] + [:nav.library-header-navigation + [:a.library-header-navigation-item "Icons"] + [:a.library-header-navigation-item "Images"] + [:a.library-header-navigation-item "Colors"]]])) + +(mf/defc library-sidebar + [] + [:aside.library-sidebar + [:button.library-sidebar-add-item + {:type "button"} + "+ New icon library"] + [:ul.library-sidebar-list + [:li.library-sidebar-list-element [:a "Ecometer"]] + [:li.library-sidebar-list-element [:a "Tipi"]] + [:li.library-sidebar-list-element [:a "Taiga (inspirational)"]] + [:li.library-sidebar-list-element [:a "DKT photo assets"]]]]) + +(mf/defc library-top-menu + [] + (let [state (mf/use-state {:is-open false})] + [:header.library-top-menu + [:div.library-top-menu-current-element + [:h2.library-top-menu-current-element-name "UXBOX"] + [:a.library-top-menu-current-action + { :on-click #(swap! state update :is-open not)} + [:span i/arrow-down]] + [:& context-menu {:is-open (:is-open @state) + :options [["Rename" #(println "Rename")] + ["Delete" #(println "Delete")]]}]] + + [:div.library-top-menu-actions + [:a i/trash] + [:a.btn-dashboard "+ New icon"]]])) + +(mf/defc library-icon-card [] + (let [state (mf/use-state {:is-open false})] + [:div.library-card.library-icon + [:div.input-checkbox.check-primary + [:input {:type "checkbox" + :id "card" + :on-change #(println "toggle-selection") + #_(:checked false)}] + [:label {:for "card"}]] + [:div.library-card-image i/trash] + [:div.library-card-footer + [:div.library-card-footer-name "my-dear-icon.svg"] + [:div.library-card-footer-timestamp "Less than 5 seconds ago"] + [:div.library-card-footer-menu + { :on-click #(swap! state update :is-open not) } + i/actions] + [:& context-menu {:is-open (:is-open @state) + :options [["Delete" #(println "Delete")]]}]]])) + +(mf/defc library-image-card [] + (let [state (mf/use-state {:is-open false})] + [:div.library-card.library-image + [:div.input-checkbox.check-primary + [:input {:type "checkbox" + :id "card" + :on-change #(println "toggle-selection") + #_(:checked false)}] + [:label {:for "card"}]] + [:div.library-card-image + [:img {:src "https://www.placecage.com/200/200"}]] + [:div.library-card-footer + [:div.library-card-footer-name "my-dear-icon.svg"] + [:div.library-card-footer-timestamp "Less than 5 seconds ago"] + [:div.library-card-footer-menu + { :on-click #(swap! state update :is-open not) } + i/actions] + [:& context-menu {:is-open (:is-open @state) + :options [["Delete" #(println "Delete")]]}]]])) + +(mf/defc library-color-card [] + (let [state (mf/use-state {:is-open false})] + [:div.library-card.library-color + [:div.input-checkbox.check-primary + [:input {:type "checkbox" + :id "card" + :on-change #(println "toggle-selection") + #_(:checked false)}] + [:label {:for "card"}]] + [:div.library-card-image + { :style { :background-color "#9B78FF" }}] + [:div.library-card-footer + #_[:* + [:div.library-card-footer-name "my-dear-icon.svg"] + [:div.library-card-footer-timestamp "Less than 5 seconds ago"]] + [:* + [:div.library-card-footer-name "#9B78FF"] + [:div.library-card-footer-color + [:span.library-card-footer-color-label "RGB"] + [:span.library-card-footer-color-rgb "128, 128, 128"]]] + [:div.library-card-footer-menu + { :on-click #(swap! state update :is-open not) } + i/actions] + [:& context-menu {:is-open (:is-open @state) + :options [["Delete" #(println "Delete")]]}]]])) + +(mf/defc library-page + [{:keys [team-id]}] + [:div.library-page + [:& library-header] + [:& library-sidebar] + [:section.library-content + [:& library-top-menu] + [:div.library-page-cards-container + (for [_ (range 0 10)] + #_[:& library-icon-card] + #_[:& library-image-card] + [:& library-color-card])]]]) diff --git a/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs b/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs index 50210b3361..b781330ba4 100644 --- a/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/recent_files.cljs @@ -39,7 +39,14 @@ (-> (l/key :recent-files) (l/derive st/state))) -;; --- Component: Drafts Page +;; --- Component: Recent files + +(mf/defc recent-files-header + [{:keys [profile] :as props}] + (let [locale (i18n/use-locale)] + [:header#main-bar.main-bar + [:h1.dashboard-title "Recent"] + [:a.btn-dashboard "+ New project"]])) (mf/defc recent-project [{:keys [project files first? locale] :as props}] @@ -70,11 +77,13 @@ recent-files (mf/deref recent-files-ref) locale (i18n/use-locale)] (when (and projects recent-files) - [:section.recent-files-page - (for [project projects] - [:& recent-project {:project project - :locale locale - :key (:id project) - :files (get recent-files (:id project)) - :first? (= project (first projects))}])]))) + [:* + [:& recent-files-header] + [:section.recent-files-page + (for [project projects] + [:& recent-project {:project project + :locale locale + :key (:id project) + :files (get recent-files (:id project)) + :first? (= project (first projects))}])]]))) diff --git a/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs b/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs index 5bdcc5debe..5a28e755d9 100644 --- a/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs @@ -24,7 +24,6 @@ [uxbox.main.ui.keyboard :as kbd] [uxbox.main.ui.confirm :refer [confirm-dialog]] [uxbox.main.ui.dashboard.common :as common] - [uxbox.main.ui.dashboard.header :refer [header]] [uxbox.main.ui.messages :refer [messages-widget]] [uxbox.util.dom :as dom] [uxbox.util.i18n :as i18n :refer [t tr]] @@ -96,6 +95,8 @@ drafts? (and (= selected-section :dashboard-project) (= selected-team-id (:default-team-id profile)) (= selected-project-id (:default-project-id profile))) + library? (and (= selected-section :dashboard-library) + (= selected-team-id (:default-team-id profile))) locale (i18n/use-locale)] [:ul.library-elements [:li.recent-projects @@ -113,6 +114,8 @@ [:li + {:on-click #(st/emit! (rt/nav :dashboard-library {:team-id team-id})) + :class-name (when library? "current")} i/icon-set [:span.element-title (t locale "dashboard.sidebar.libraries")]] From b2474fc3fbfa7ebc4c9cc69ef14c6e7630903359 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 16 Mar 2020 15:52:02 +0100 Subject: [PATCH 2/3] :neckbeard: retrieve data from backend --- backend/src/uxbox/config.clj | 2 + backend/src/uxbox/fixtures.clj | 15 +- backend/src/uxbox/services/queries/icons.clj | 1 + backend/src/uxbox/services/queries/images.clj | 1 + frontend/resources/locales.json | 102 ++- .../styles/main/layouts/library-page.scss | 24 +- frontend/src/uxbox/main/data/colors.cljs | 46 ++ frontend/src/uxbox/main/data/icons.cljs | 509 +++++++------ frontend/src/uxbox/main/data/images.cljs | 43 ++ frontend/src/uxbox/main/ui.cljs | 22 +- frontend/src/uxbox/main/ui/dashboard.cljs | 33 +- .../src/uxbox/main/ui/dashboard/icons.cljs | 708 +++++++++--------- .../src/uxbox/main/ui/dashboard/library.cljs | 193 +++-- .../src/uxbox/main/ui/dashboard/sidebar.cljs | 4 +- .../main/ui/workspace/sidebar/icons.cljs | 6 +- 15 files changed, 1022 insertions(+), 687 deletions(-) diff --git a/backend/src/uxbox/config.clj b/backend/src/uxbox/config.clj index 7c5199dbb2..6659598690 100644 --- a/backend/src/uxbox/config.clj +++ b/backend/src/uxbox/config.clj @@ -23,6 +23,8 @@ {:http-server-port 6060 :http-server-cors "http://localhost:3449" :database-uri "postgresql://127.0.0.1/uxbox" + :database-username "uxbox" + :database-password "uxbox" :media-directory "resources/public/media" :assets-directory "resources/public/static" :media-uri "http://localhost:6060/media/" diff --git a/backend/src/uxbox/fixtures.clj b/backend/src/uxbox/fixtures.clj index ae9f82a663..d3c65dacc8 100644 --- a/backend/src/uxbox/fixtures.clj +++ b/backend/src/uxbox/fixtures.clj @@ -66,9 +66,20 @@ values ($1, $2, $3, $4, $5, $6) returning id;") +(def sql:create-icon-library + "insert into icon_library (team_id, name) + values ($1, $2) + returning id;") + +(def sql:create-icon + "insert into icon_library (library_id, name, content, metadata) + values ($1, $2, $3, $4) + returning id;") + + (def preset-small - {:num-teams 50 - :num-profiles 50 + {:num-teams 5 + :num-profiles 5 :num-profiles-per-team 5 :num-projects-per-team 5 :num-files-per-project 5 diff --git a/backend/src/uxbox/services/queries/icons.clj b/backend/src/uxbox/services/queries/icons.clj index 08688a14cd..b1922f37fe 100644 --- a/backend/src/uxbox/services/queries/icons.clj +++ b/backend/src/uxbox/services/queries/icons.clj @@ -31,6 +31,7 @@ (s/def ::name ::us/string) (s/def ::profile-id ::us/uuid) (s/def ::library-id ::us/uuid) +(s/def ::team-id ::us/uuid) (defn decode-row [{:keys [metadata] :as row}] diff --git a/backend/src/uxbox/services/queries/images.clj b/backend/src/uxbox/services/queries/images.clj index 1243bf8d09..a690a5cc1d 100644 --- a/backend/src/uxbox/services/queries/images.clj +++ b/backend/src/uxbox/services/queries/images.clj @@ -21,6 +21,7 @@ (s/def ::id ::us/uuid) (s/def ::name ::us/string) (s/def ::profile-id ::us/uuid) +(s/def ::team-id ::us/uuid) (s/def ::library-id ::us/uuid) ;; --- Query: Image Librarys diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index 1ae0c53232..b1247c6058 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -78,14 +78,68 @@ "en" : "Type to search results" } }, + "dashboard.library.add-item.icons" : { + "translations" : { + "en" : "+ New icon" + }, + "unused" : true + }, + "dashboard.library.add-item.images" : { + "translations" : { + "en" : "+ New image" + }, + "unused" : true + }, + "dashboard.library.add-item.palettes" : { + "translations" : { + "en" : "+ New color" + }, + "unused" : true + }, + "dashboard.library.add-library.icons" : { + "translations" : { + "en" : "+ New icon library" + }, + "unused" : true + }, + "dashboard.library.add-library.images" : { + "translations" : { + "en" : "+ New image library" + }, + "unused" : true + }, + "dashboard.library.add-library.palettes" : { + "translations" : { + "en" : "+ New palette" + }, + "unused" : true + }, + "dashboard.library.menu.icons" : { + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:39" ], + "translations" : { + "en" : "Icons" + } + }, + "dashboard.library.menu.images" : { + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:43" ], + "translations" : { + "en" : "Images" + } + }, + "dashboard.library.menu.palettes" : { + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:47" ], + "translations" : { + "en" : "Palettes" + } + }, "dashboard.sidebar.drafts" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:113" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:112" ], "translations" : { "en" : "Drafts" } }, "dashboard.sidebar.libraries" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:118" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:117" ], "translations" : { "en" : "Libraries" } @@ -97,7 +151,7 @@ "unused" : true }, "dashboard.sidebar.recent" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:106" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:105" ], "translations" : { "en" : "Recent" } @@ -109,6 +163,24 @@ "fr" : "Accepter" } }, + "ds.button.delete" : { + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:80", "src/uxbox/main/ui/dashboard/library.cljs:111", "src/uxbox/main/ui/dashboard/library.cljs:133", "src/uxbox/main/ui/dashboard/library.cljs:157" ], + "translations" : { + "en" : "Delete" + } + }, + "ds.button.rename" : { + "used-in" : [ "src/uxbox/main/ui/dashboard/library.cljs:79" ], + "translations" : { + "en" : "Rename" + } + }, + "ds.button.save" : { + "translations" : { + "en" : "Save" + }, + "unused" : true + }, "ds.cancel" : { "used-in" : [ "src/uxbox/main/ui/workspace/sidebar/history.cljs:114" ], "translations" : { @@ -166,7 +238,7 @@ } }, "ds.default-library-title" : { - "used-in" : [ "src/uxbox/main/data/icons.cljs:90", "src/uxbox/main/data/colors.cljs:68", "src/uxbox/main/data/images.cljs:110" ], + "used-in" : [ "src/uxbox/main/data/colors.cljs:68", "src/uxbox/main/data/icons.cljs:90", "src/uxbox/main/data/images.cljs:110" ], "translations" : { "en" : "Unnamed Collection (%s)", "fr" : "Collection sans nom (%s)" @@ -244,7 +316,7 @@ } }, "ds.multiselect-bar.delete" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/icons.cljs:221", "src/uxbox/main/ui/dashboard/colors.cljs:214", "src/uxbox/main/ui/dashboard/images.cljs:187" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/colors.cljs:214", "src/uxbox/main/ui/dashboard/images.cljs:187", "src/uxbox/main/ui/dashboard/icons.cljs:221" ], "translations" : { "en" : "Delete", "fr" : "Supprimer" @@ -300,7 +372,7 @@ "unused" : true }, "ds.search.placeholder" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:168" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/sidebar.cljs:140" ], "translations" : { "en" : "Search...", "fr" : "Rechercher..." @@ -356,7 +428,7 @@ } }, "ds.uploaded-at" : { - "used-in" : [ "src/uxbox/main/ui/dashboard/icons.cljs:309", "src/uxbox/main/ui/dashboard/images.cljs:271" ], + "used-in" : [ "src/uxbox/main/ui/dashboard/images.cljs:271", "src/uxbox/main/ui/dashboard/icons.cljs:309" ], "translations" : { "en" : "Uploaded at %s", "fr" : "Mise en ligne : %s" @@ -426,14 +498,14 @@ } }, "errors.generic" : { - "used-in" : [ "src/uxbox/main/ui.cljs:135" ], + "used-in" : [ "src/uxbox/main/ui.cljs:131" ], "translations" : { "en" : "Something wrong has happened.", "fr" : "Quelque chose c'est mal passé." } }, "errors.network" : { - "used-in" : [ "src/uxbox/main/ui.cljs:129" ], + "used-in" : [ "src/uxbox/main/ui.cljs:125" ], "translations" : { "en" : "Unable to connect to backend server.", "fr" : "Impossible de se connecter au serveur principal." @@ -538,7 +610,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!" @@ -895,7 +967,7 @@ } }, "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" @@ -937,7 +1009,7 @@ } }, "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:69" ], "translations" : { "en" : "Size, position & rotation", "fr" : "Taille, position et rotation" @@ -951,21 +1023,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:98" ], "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:115" ], "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:71" ], "translations" : { "en" : "Size", "fr" : "Taille" diff --git a/frontend/resources/styles/main/layouts/library-page.scss b/frontend/resources/styles/main/layouts/library-page.scss index 2b169471b9..2bc0746f69 100644 --- a/frontend/resources/styles/main/layouts/library-page.scss +++ b/frontend/resources/styles/main/layouts/library-page.scss @@ -47,6 +47,8 @@ .library-sidebar { background-color: $color-white; padding: $size-2; + height: 100%; + overflow: hidden; } .library-sidebar-add-item { @@ -66,11 +68,17 @@ .library-sidebar-list { margin-top: $size-4; + overflow: scroll; + height: 100%; + padding-bottom: 4rem; } .library-sidebar-list-element { padding: $size-4 $size-2; cursor: pointer; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; & a { color: $color-black; @@ -138,17 +146,19 @@ } .library-page-cards-container { - padding: $size-2; + align-content: flex-start; display: flex; flex-wrap: wrap; - overflow: scroll; height: 100%; - align-content: flex-start; + overflow: scroll; + padding: $size-2; + padding-bottom: 4rem; } .library-card { margin: $size-2; background: $color-white; + border: 2px solid transparent; &.library-icon, &.library-color { width: 200px; @@ -167,7 +177,11 @@ visibility: hidden; opacity: 0; } - + + &:hover { + border: 2px solid $color-primary; + } + &:hover .library-card-footer-menu { visibility: visible; opacity: 1; @@ -204,7 +218,7 @@ text-align: center; display: flex; - & svg, & img { + & object, & svg, & img { margin: auto; width: auto; height: auto; diff --git a/frontend/src/uxbox/main/data/colors.cljs b/frontend/src/uxbox/main/data/colors.cljs index 69a0115771..cdfed29bd2 100644 --- a/frontend/src/uxbox/main/data/colors.cljs +++ b/frontend/src/uxbox/main/data/colors.cljs @@ -6,9 +6,11 @@ (ns uxbox.main.data.colors (:require + [cljs.spec.alpha :as s] [beicon.core :as rx] [clojure.set :as set] [potok.core :as ptk] + [uxbox.common.spec :as us] [uxbox.main.repo :as rp] [uxbox.main.store :as st] [uxbox.util.color :as color] @@ -241,3 +243,47 @@ (defn delete-colors [coll-id colors] (DeleteColors. coll-id colors)) + + +;;;; NEW + +(declare fetch-color-libraries-result) + +(defn fetch-color-libraries + [team-id] + (s/assert ::us/uuid team-id) + (ptk/reify ::fetch-color-libraries + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/query! :color-libraries {:team-id team-id}) + (rx/map fetch-color-libraries-result))))) + +(defn fetch-color-libraries-result [result] + (ptk/reify ::fetch-color-libraries-result + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :color-libraries] result))))) + +(declare fetch-color-library-result) + +(defn fetch-color-library + [library-id] + (ptk/reify ::fetch-color-library + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :selected-items] nil))) + + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/query! :colors {:library-id library-id}) + (rx/map fetch-color-library-result))))) + +(defn fetch-color-library-result + [data] + (ptk/reify ::fetch-color-library + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :selected-items] data))))) diff --git a/frontend/src/uxbox/main/data/icons.cljs b/frontend/src/uxbox/main/data/icons.cljs index 6c3ccc60d4..a7ac82f153 100644 --- a/frontend/src/uxbox/main/data/icons.cljs +++ b/frontend/src/uxbox/main/data/icons.cljs @@ -35,20 +35,61 @@ ::user-id])) -(declare fetch-icons) - -(defn initialize - [collection-id] - (s/assert ::us/uuid collection-id) - (ptk/reify ::initialize - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:dashboard-icons :selected] #{})) +(declare fetch-icon-libraries-result) +(defn fetch-icon-libraries + [team-id] + (s/assert ::us/uuid team-id) + (ptk/reify ::fetch-icon-libraries ptk/WatchEvent (watch [_ state stream] - (rx/of (fetch-icons collection-id))))) + (->> (rp/query! :icon-libraries {:team-id team-id}) + (rx/map fetch-icon-libraries-result))))) +(defn fetch-icon-libraries-result [result] + (ptk/reify ::fetch-icon-libraries-result + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :icon-libraries] result))))) + +(declare fetch-icon-library-result) + +(defn fetch-icon-library + [library-id] + (ptk/reify ::fetch-icon-library + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :selected-items] nil))) + + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/query! :icons {:library-id library-id}) + (rx/map fetch-icon-library-result))))) + +(defn fetch-icon-library-result + [data] + (ptk/reify ::fetch-icon-library + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :selected-items] data))))) + +;; (declare fetch-icons) +;; +;; (defn initialize +;; [collection-id] +;; (s/assert ::us/uuid collection-id) +;; (ptk/reify ::initialize +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:dashboard-icons :selected] #{})) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (fetch-icons collection-id))))) +;; ;; --- Fetch Collections (declare collections-fetched) @@ -79,156 +120,156 @@ items)))) -;; --- Create Collection - -(declare collection-created) - -(def create-collection - (ptk/reify ::create-collection - ptk/WatchEvent - (watch [_ state s] - (let [name (tr "ds.default-library-title" (gensym "c")) - data {:name name}] - (->> (rp/mutation! :create-icons-collection data) - (rx/map collection-created)))))) - - -;; --- Collection Created - -(defn collection-created - [item] - (s/assert ::collection item) - (ptk/reify ::collection-created - ptk/UpdateEvent - (update [_ state] - (let [{:keys [id] :as item} (assoc item :type :own)] - (update state :icons-collections assoc id item))))) - -;; --- Rename Collection - -(defn rename-collection - [id name] - (ptk/reify ::rename-collection - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:icons-collections id :name] name)) - - ptk/WatchEvent - (watch [_ state s] - (let [params {:id id :name name}] - (->> (rp/mutation! :rename-icons-collection params) - (rx/ignore)))))) - -;; --- Delete Collection - -(defn delete-collection - [id on-success] - (ptk/reify ::delete-collection - ptk/UpdateEvent - (update [_ state] - (update state :icons-collections dissoc id)) - - ptk/WatchEvent - (watch [_ state s] - (->> (rp/mutation! :delete-icons-collection {:id id}) - (rx/tap on-success) - (rx/ignore))))) - -;; --- Icon Created - -(defrecord IconCreated [item] - ptk/UpdateEvent - (update [_ state] - (let [{:keys [id] :as item} (assoc item :type :icon)] - (update state :icons assoc id item)))) - -(defn icon-created - [item] - (IconCreated. item)) - -;; --- Create Icon - -(declare icon-created) - -(defn- parse-svg - [data] - (s/assert ::us/string data) - (let [valid-tags #{"defs" "path" "circle" "rect" "metadata" "g" - "radialGradient" "stop"} - div (dom/create-element "div") - gc (dom/create-element "div") - g (dom/create-element "http://www.w3.org/2000/svg" "g") - _ (dom/set-html! div data) - svg (dom/query div "svg")] - (loop [child (dom/get-first-child svg)] - (if child - (let [tagname (dom/get-tag-name child)] - (if (contains? valid-tags tagname) - (dom/append-child! g child) - (dom/append-child! gc child)) - (recur (dom/get-first-child svg))) - (let [width (.. svg -width -baseVal -value) - height (.. svg -height -baseVal -value) - view-box [(.. svg -viewBox -baseVal -x) - (.. svg -viewBox -baseVal -y) - (.. svg -viewBox -baseVal -width) - (.. svg -viewBox -baseVal -height)] - props {:width width - :mimetype "image/svg+xml" - :height height - :view-box view-box}] - [(dom/get-outer-html g) props]))))) - - -(defn create-icons - [id files] - (s/assert (s/nilable uuid?) id) - (ptk/reify ::create-icons - ptk/WatchEvent - (watch [_ state s] - (letfn [(parse [file] - (->> (wapi/read-file-as-text file) - (rx/map parse-svg))) - (allowed? [file] - (= (.-type file) "image/svg+xml")) - (prepare [[content metadata]] - {:collection-id id - :content content - :id (uuid/next) - ;; TODO Keep the name of the original icon - :name (str "Icon " (gensym "i")) - :metadata metadata})] - (->> (rx/from files) - (rx/filter allowed?) - (rx/merge-map parse) - (rx/map prepare) - (rx/flat-map #(rp/mutation! :create-icon %)) - (rx/map icon-created)))))) - -;; --- Icon Persisted - -(defrecord IconPersisted [id data] - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:icons id] data))) - -(defn icon-persisted - [{:keys [id] :as data}] - {:pre [(map? data)]} - (IconPersisted. id data)) - -;; --- Persist Icon - -(defn persist-icon - [id] - (s/assert ::us/uuid id) - (ptk/reify ::persist-icon - ptk/WatchEvent - (watch [_ state stream] - (let [data (get-in state [:icons id])] - (->> (rp/mutation! :update-icon data) - (rx/ignore)))))) - +;; ;; --- Create Collection +;; +;; (declare collection-created) +;; +;; (def create-collection +;; (ptk/reify ::create-collection +;; ptk/WatchEvent +;; (watch [_ state s] +;; (let [name (tr "ds.default-library-title" (gensym "c")) +;; data {:name name}] +;; (->> (rp/mutation! :create-icons-collection data) +;; (rx/map collection-created)))))) +;; +;; +;; ;; --- Collection Created +;; +;; (defn collection-created +;; [item] +;; (s/assert ::collection item) +;; (ptk/reify ::collection-created +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [{:keys [id] :as item} (assoc item :type :own)] +;; (update state :icons-collections assoc id item))))) +;; +;; ;; --- Rename Collection +;; +;; (defn rename-collection +;; [id name] +;; (ptk/reify ::rename-collection +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:icons-collections id :name] name)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (let [params {:id id :name name}] +;; (->> (rp/mutation! :rename-icons-collection params) +;; (rx/ignore)))))) +;; +;; ;; --- Delete Collection +;; +;; (defn delete-collection +;; [id on-success] +;; (ptk/reify ::delete-collection +;; ptk/UpdateEvent +;; (update [_ state] +;; (update state :icons-collections dissoc id)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (->> (rp/mutation! :delete-icons-collection {:id id}) +;; (rx/tap on-success) +;; (rx/ignore))))) +;; +;; ;; --- Icon Created +;; +;; (defrecord IconCreated [item] +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [{:keys [id] :as item} (assoc item :type :icon)] +;; (update state :icons assoc id item)))) +;; +;; (defn icon-created +;; [item] +;; (IconCreated. item)) +;; +;; ;; --- Create Icon +;; +;; (declare icon-created) +;; +;; (defn- parse-svg +;; [data] +;; (s/assert ::us/string data) +;; (let [valid-tags #{"defs" "path" "circle" "rect" "metadata" "g" +;; "radialGradient" "stop"} +;; div (dom/create-element "div") +;; gc (dom/create-element "div") +;; g (dom/create-element "http://www.w3.org/2000/svg" "g") +;; _ (dom/set-html! div data) +;; svg (dom/query div "svg")] +;; (loop [child (dom/get-first-child svg)] +;; (if child +;; (let [tagname (dom/get-tag-name child)] +;; (if (contains? valid-tags tagname) +;; (dom/append-child! g child) +;; (dom/append-child! gc child)) +;; (recur (dom/get-first-child svg))) +;; (let [width (.. svg -width -baseVal -value) +;; height (.. svg -height -baseVal -value) +;; view-box [(.. svg -viewBox -baseVal -x) +;; (.. svg -viewBox -baseVal -y) +;; (.. svg -viewBox -baseVal -width) +;; (.. svg -viewBox -baseVal -height)] +;; props {:width width +;; :mimetype "image/svg+xml" +;; :height height +;; :view-box view-box}] +;; [(dom/get-outer-html g) props]))))) +;; +;; +;; (defn create-icons +;; [id files] +;; (s/assert (s/nilable uuid?) id) +;; (ptk/reify ::create-icons +;; ptk/WatchEvent +;; (watch [_ state s] +;; (letfn [(parse [file] +;; (->> (wapi/read-file-as-text file) +;; (rx/map parse-svg))) +;; (allowed? [file] +;; (= (.-type file) "image/svg+xml")) +;; (prepare [[content metadata]] +;; {:collection-id id +;; :content content +;; :id (uuid/next) +;; ;; TODO Keep the name of the original icon +;; :name (str "Icon " (gensym "i")) +;; :metadata metadata})] +;; (->> (rx/from files) +;; (rx/filter allowed?) +;; (rx/merge-map parse) +;; (rx/map prepare) +;; (rx/flat-map #(rp/mutation! :create-icon %)) +;; (rx/map icon-created)))))) +;; +;; ;; --- Icon Persisted +;; +;; (defrecord IconPersisted [id data] +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:icons id] data))) +;; +;; (defn icon-persisted +;; [{:keys [id] :as data}] +;; {:pre [(map? data)]} +;; (IconPersisted. id data)) +;; +;; ;; --- Persist Icon +;; +;; (defn persist-icon +;; [id] +;; (s/assert ::us/uuid id) +;; (ptk/reify ::persist-icon +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (let [data (get-in state [:icons id])] +;; (->> (rp/mutation! :update-icon data) +;; (rx/ignore)))))) +;; ;; --- Load Icons (declare icons-fetched) @@ -253,80 +294,80 @@ (let [icons (d/index-by :id items)] (assoc state :icons icons))))) -;; --- Rename Icon - -(defn rename-icon - [id name] - (s/assert ::us/uuid id) - (s/assert ::us/string name) - (ptk/reify ::rename-icon - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:icons id :name] name)) - - ptk/WatchEvent - (watch [_ state stream] - (rx/of (persist-icon id))))) - -;; --- Icon Selection - -(defn select-icon - [id] - (ptk/reify ::select-icon - ptk/UpdateEvent - (update [_ state] - (update-in state [:dashboard-icons :selected] (fnil conj #{}) id)))) - -(defn deselect-icon - [id] - (ptk/reify ::deselect-icon - ptk/UpdateEvent - (update [_ state] - (update-in state [:dashboard-icons :selected] (fnil disj #{}) id)))) - -(def deselect-all-icons - (ptk/reify ::deselect-all-icons - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:dashboard-icons :selected] #{})))) - -;; --- Delete Icons - -(defn delete-icon - [id] - (ptk/reify ::delete-icon - ptk/UpdateEvent - (update [_ state] - (update state :icons dissoc id)) - - ptk/WatchEvent - (watch [_ state s] - (rx/merge - (rx/of deselect-all-icons) - (->> (rp/mutation! :delete-icon {:id id}) - (rx/ignore)))))) - -;; --- Delete Selected - -(def delete-selected - (ptk/reify ::delete-selected - ptk/WatchEvent - (watch [_ state stream] - (let [selected (get-in state [:dashboard-icons :selected])] - (->> (rx/from selected) - (rx/map delete-icon)))))) -;; --- Update Opts (Filtering & Ordering) - -(defn update-opts - [& {:keys [order filter edition] - :or {edition false}}] - (ptk/reify ::update-opts - ptk/UpdateEvent - (update [_ state] - (update state :dashboard-icons merge - {:edition edition} - (when order {:order order}) - (when filter {:filter filter}))))) +;; ;; --- Rename Icon +;; +;; (defn rename-icon +;; [id name] +;; (s/assert ::us/uuid id) +;; (s/assert ::us/string name) +;; (ptk/reify ::rename-icon +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:icons id :name] name)) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (persist-icon id))))) +;; +;; ;; --- Icon Selection +;; +;; (defn select-icon +;; [id] +;; (ptk/reify ::select-icon +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:dashboard-icons :selected] (fnil conj #{}) id)))) +;; +;; (defn deselect-icon +;; [id] +;; (ptk/reify ::deselect-icon +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:dashboard-icons :selected] (fnil disj #{}) id)))) +;; +;; (def deselect-all-icons +;; (ptk/reify ::deselect-all-icons +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:dashboard-icons :selected] #{})))) +;; +;; ;; --- Delete Icons +;; +;; (defn delete-icon +;; [id] +;; (ptk/reify ::delete-icon +;; ptk/UpdateEvent +;; (update [_ state] +;; (update state :icons dissoc id)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (rx/merge +;; (rx/of deselect-all-icons) +;; (->> (rp/mutation! :delete-icon {:id id}) +;; (rx/ignore)))))) +;; +;; ;; --- Delete Selected +;; +;; (def delete-selected +;; (ptk/reify ::delete-selected +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (let [selected (get-in state [:dashboard-icons :selected])] +;; (->> (rx/from selected) +;; (rx/map delete-icon)))))) +;; ;; --- Update Opts (Filtering & Ordering) +;; +;; (defn update-opts +;; [& {:keys [order filter edition] +;; :or {edition false}}] +;; (ptk/reify ::update-opts +;; ptk/UpdateEvent +;; (update [_ state] +;; (update state :dashboard-icons merge +;; {:edition edition} +;; (when order {:order order}) +;; (when filter {:filter filter}))))) ;; --- Copy Selected Icon diff --git a/frontend/src/uxbox/main/data/images.cljs b/frontend/src/uxbox/main/data/images.cljs index 99f2ac642f..bbdbc881da 100644 --- a/frontend/src/uxbox/main/data/images.cljs +++ b/frontend/src/uxbox/main/data/images.cljs @@ -398,3 +398,46 @@ ;; {:pre [(or (uuid? id) (nil? id))]} ;; (MoveSelected. id)) + +;;;;;;; NEW + +(declare fetch-image-libraries-result) + +(defn fetch-image-libraries + [team-id] + (s/assert ::us/uuid team-id) + (ptk/reify ::fetch-image-libraries + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/query! :image-libraries {:team-id team-id}) + (rx/map fetch-image-libraries-result))))) + +(defn fetch-image-libraries-result [result] + (ptk/reify ::fetch-image-libraries-result + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :image-libraries] result))))) + +(declare fetch-image-library-result) + +(defn fetch-image-library + [library-id] + (ptk/reify ::fetch-image-library + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :selected-items] nil))) + + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/query! :image {:library-id library-id}) + (rx/map fetch-image-library-result))))) + +(defn fetch-image-library-result + [data] + (ptk/reify ::fetch-image-library + ptk/UpdateEvent + (update [_ state] + (-> state + (assoc-in [:library :selected-items] data))))) diff --git a/frontend/src/uxbox/main/ui.cljs b/frontend/src/uxbox/main/ui.cljs index bdc69b7173..0c96fa476a 100644 --- a/frontend/src/uxbox/main/ui.cljs +++ b/frontend/src/uxbox/main/ui.cljs @@ -56,7 +56,20 @@ ["/" :dashboard-team] ["/search" :dashboard-search] ["/project/:project-id" :dashboard-project] - ["/library" :dashboard-library]]] + ["/library" + ["/icons" + ["" { :name :dashboard-library-icons-index :section :icons}] + ["/:library-id" { :name :dashboard-library-icons :section :icons}]] + + ["/images" + ["" { :name :dashboard-library-images-index :section :images}] + ["/:library-id" { :name :dashboard-library-images :section :images}]] + + ["/palettes" + ["" { :name :dashboard-library-palettes-index :section :palettes}] + ["/:library-id" { :name :dashboard-library-palettes :section :palettes }]] + + ]]] ["/workspace/:file-id" :workspace]]) @@ -91,7 +104,12 @@ (:dashboard-search :dashboard-team :dashboard-project - :dashboard-library) + :dashboard-library-icons + :dashboard-library-icons-index + :dashboard-library-images + :dashboard-library-images-index + :dashboard-library-palettes + :dashboard-library-palettes-index) (mf/element dashboard #js {:route route}) :workspace diff --git a/frontend/src/uxbox/main/ui/dashboard.cljs b/frontend/src/uxbox/main/ui/dashboard.cljs index 90a3b02b2d..a1377ec4d8 100644 --- a/frontend/src/uxbox/main/ui/dashboard.cljs +++ b/frontend/src/uxbox/main/ui/dashboard.cljs @@ -32,8 +32,10 @@ (defn- parse-params [route profile] (let [search-term (get-in route [:params :query :search-term]) + route-name (get-in route [:data :name]) team-id (get-in route [:params :path :team-id]) - project-id (get-in route [:params :path :project-id])] + project-id (get-in route [:params :path :project-id]) + library-id (get-in route [:params :path :library-id])] (cond-> {:search-term search-term} @@ -48,14 +50,21 @@ (and (= "drafts" project-id) (= "self" team-id)) - (assoc :project-id (:default-project-id profile))))) + (assoc :project-id (:default-project-id profile)) + + (str/starts-with? (name route-name) "dashboard-library") + (assoc :library-section (get-in route [:data :section])) + + (uuid-str? library-id) + (assoc :library-id (uuid library-id))))) (mf/defc dashboard [{:keys [route] :as props}] (let [profile (mf/deref refs/profile) - section (get-in route [:data :name]) - {:keys [search-term team-id project-id]} (parse-params route profile)] + page (get-in route [:data :name]) + {:keys [search-term team-id project-id library-id library-section] :as params} + (parse-params route profile)] [:main.dashboard-main [:& messages-widget] [:section.dashboard-layout @@ -63,18 +72,24 @@ [:& profile-section {:profile profile}] [:& sidebar {:team-id team-id :project-id project-id - :search-term search-term - :section section}] + :section page}] [:div.dashboard-content - (case section + (case page :dashboard-search (mf/element search-page #js {:team-id team-id :search-term search-term}) :dashboard-team (mf/element recent-files-page #js {:team-id team-id}) - :dashboard-library - (mf/element library-page #js {:team-id team-id}) + (:dashboard-library-icons + :dashboard-library-icons-index + :dashboard-library-images + :dashboard-library-images-index + :dashboard-library-palettes + :dashboard-library-palettes-index) + (mf/element library-page #js {:team-id team-id + :library-id library-id + :section library-section}) :dashboard-project (mf/element project-page #js {:team-id team-id diff --git a/frontend/src/uxbox/main/ui/dashboard/icons.cljs b/frontend/src/uxbox/main/ui/dashboard/icons.cljs index c1df4f7147..6381a39fad 100644 --- a/frontend/src/uxbox/main/ui/dashboard/icons.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/icons.cljs @@ -28,357 +28,357 @@ [uxbox.util.router :as rt] [uxbox.util.time :as dt])) -;; --- Helpers & Constants - -(def +ordering-options+ - {:name "ds.ordering.by-name" - :created "ds.ordering.by-creation-date"}) - -(defn- sort-icons-by - [ordering icons] - (case ordering - :name (sort-by :name icons) - :created (reverse (sort-by :created-at icons)) - icons)) - -(defn- contains-term? - [phrase term] - {:pre [(string? phrase) - (string? term)]} - (let [term (name term)] - (str/includes? (str/lower phrase) (str/trim (str/lower term))))) - -(defn- filter-icons-by - [term icons] - (if (str/blank? term) - icons - (filter #(contains-term? (:name %) term) icons))) - -;; --- Component: Grid Header - -(mf/defc grid-header - [{:keys [collection] :as props}] - (let [{:keys [id type]} collection - on-change #(st/emit! (di/rename-collection id %)) - on-deleted #(st/emit! (rt/nav :dashboard-icons nil {:type type})) - delete #(st/emit! (di/delete-collection id on-deleted)) - on-delete #(modal/show! confirm-dialog {:on-accept delete})] - [:& common/grid-header {:value (:name collection) - :on-change on-change - :on-delete on-delete}])) - -;; --- Nav - -(mf/defc nav-item - [{:keys [collection selected?] :as props}] - (let [local (mf/use-state {}) - {:keys [id type name]} collection - editable? (= type :own) - - on-click - (fn [event] - (let [type (or type :own)] - (st/emit! (rt/nav :dashboard-icons {} {:type type :id id})))) - - - on-input-change - (fn [event] - (-> (dom/get-target event) - (dom/get-value) - (swap! local assoc :name))) - - on-cancel #(swap! local dissoc :name :edit) - on-double-click #(when editable? (swap! local assoc :edit true)) - - on-input-keyup - (fn [event] - (when (kbd/enter? event) - (let [value (-> (dom/get-target event) (dom/get-value))] - (st/emit! (di/rename-collection id (str/trim (:name @local)))) - (swap! local assoc :edit false))))] - - [:li {:on-click on-click - :on-double-click on-double-click - :class-name (when selected? "current")} - (if (:edit @local) - [:div - [:input.element-title {:value (or (:name @local) name) - :on-change on-input-change - :on-key-down on-input-keyup}] - [:span.close {:on-click on-cancel} i/close]] - [:span.element-title name])])) - - -(mf/defc nav - [{:keys [id type collections] :as props}] - (let [locale (i18n/use-locale) - own? (= type :own) - builtin? (= type :builtin) - create-collection #(st/emit! di/create-collection) - select-own-tab #(st/emit! (rt/nav :dashboard-icons nil {:type :own})) - select-buitin-tab #(st/emit! (rt/nav :dashboard-icons nil {:type :builtin}))] - - [:div.library-bar - [:div.library-bar-inside - ;; Tabs - [:ul.library-tabs - [:li {:class (when own? "current") - :on-click select-own-tab} - (t locale "ds.your-icons-title")] - - [:li {:class (when builtin? "current") - :on-click select-buitin-tab} - (t locale "ds.store-icons-title")]] - - - ;; Collections List - [:ul.library-elements - (when own? - [:li - [:a.btn-primary {:on-click #(st/emit! di/create-collection)} - (tr "ds.icons-collection.new")]]) - (for [item collections] - [:& nav-item {:collection item - :selected? (= (:id item) id) - :key (:id item)}])]]])) - - -;; (mf/def grid-options-tooltip -;; :mixins [mf/reactive mf/memo] - -;; :render -;; (fn [own {:keys [selected on-select title]}] -;; {:pre [(uuid? selected) -;; (fn? on-select) -;; (string? title)]} -;; (let [colls (mf/react collections-iref) -;; colls (->> (vals colls) -;; (filter #(= :own (:type %))) -;; (remove #(= selected (:id %))) -;; (sort-by :name colls)) -;; on-select (fn [event id] -;; (dom/prevent-default event) -;; (dom/stop-propagation event) -;; (on-select id))] -;; [:ul.move-list -;; [:li.title title] -;; [:li -;; [:a {:href "#" :on-click #(on-select % nil)} "Storage"]] -;; (for [{:keys [id name] :as coll} colls] -;; [:li {:key (pr-str id)} -;; [:a {:on-click #(on-select % id)} name]])]))) - -(mf/defc grid-options - [{:keys [id type selected] :as props}] - (let [local (mf/use-state {}) - delete #(st/emit! di/delete-selected) - on-delete #(modal/show! confirm-dialog {:on-accept delete}) - - ;; (on-toggle-copy [event] - ;; (swap! local update :show-copy-tooltip not)) - ;; (on-toggle-move [event] - ;; (swap! local update :show-move-tooltip not)) - ;; (on-copy [selected] - ;; (swap! local assoc - ;; :show-move-tooltip false - ;; :show-copy-tooltip false) - ;; (st/emit! (di/copy-selected selected))) - ;; (on-move [selected] - ;; (swap! local assoc - ;; :show-move-tooltip false - ;; :show-copy-tooltip false) - ;; (st/emit! (di/move-selected selected))) - ;; (on-rename [event] - ;; (let [selected (first selected)] - ;; (st/emit! (di/update-opts :edition selected)))) - ] - ;; MULTISELECT OPTIONS BAR - [:div.multiselect-bar - (when (= type :own) - ;; If editable - [:div.multiselect-nav - ;; [:span.move-item.tooltip.tooltip-top - ;; {:alt (tr "ds.multiselect-bar.copy") - ;; :on-click on-toggle-copy} - ;; (when (:show-copy-tooltip @local) - ;; [:& grid-options-tooltip {:selected id - ;; :title (tr "ds.multiselect-bar.copy-to-library") - ;; :on-select on-copy}]) - ;; i/copy] - ;; [:span.move-item.tooltip.tooltip-top - ;; {:alt (tr "ds.multiselect-bar.move") - ;; :on-click on-toggle-move} - ;; (when (:show-move-tooltip @local) - ;; [:& grid-options-tooltip {:selected id - ;; :title (tr "ds.multiselect-bar.move-to-library") - ;; :on-select on-move}]) - ;; i/move] - ;; (when (= 1 (count selected)) - ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.rename") - ;; :on-click on-rename} - ;; i/pencil]) - [:span.delete.tooltip.tooltip-top - {:alt (tr "ds.multiselect-bar.delete") - :on-click on-delete} - i/trash]] - - ;; If not editable - ;; [:div.multiselect-nav - ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.copy") - ;; :on-click on-toggle-copy} - ;; (when (:show-copy-tooltip @local) - ;; [:& grid-options-tooltip {:selected id - ;; :title (tr "ds.multiselect-bar.copy-to-library") - ;; :on-select on-copy}]) - ;; i/organize]] - )])) - -;; --- Grid Form - -(mf/defc grid-form - [{:keys [id type uploading?] :as props}] - (let [locale (i18n/use-locale) - input (mf/use-ref nil) - on-click #(dom/click (mf/ref-node input)) - on-select #(st/emit! (->> (dom/get-target %) - (dom/get-files) - (array-seq) - (di/create-icons id)))] - [:div.grid-item.add-project {:on-click on-click} - (if uploading? - [:div i/loader-pencil] - [:span (t locale "ds.icon-new")]) - [:input.upload-icon-input - {:style {:display "none"} - :multiple true - :ref input - :value "" - :accept "icon/svg+xml" - :type "file" - :on-change on-select}]])) - -;; --- Grid Item - -(mf/defc grid-item - [{:keys [icon selected? edition?] :as props}] - (let [toggle-selection #(st/emit! (if selected? - (di/deselect-icon (:id icon)) - (di/select-icon (:id icon)))) - on-blur - (fn [event] - (let [target (dom/get-target event) - name (dom/get-value target)] - (st/emit! (di/update-opts :edition false) - (di/rename-icon (:id icon) name)))) - - on-key-down - (fn [event] - (when (kbd/enter? event) - (on-blur event))) - - ignore-click - (fn [event] - (dom/stop-propagation event) - (dom/prevent-default event)) - - on-edit - (fn [event] - (dom/stop-propagation event) - (dom/prevent-default event) - (st/emit! (di/update-opts :edition (:id icon))))] - - [:div.grid-item.small-item.project-th - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id (:id icon) - :on-change toggle-selection - :checked selected?}] - [:label {:for (:id icon)}]] - [:span.grid-item-icon - [:& icon/icon-svg {:shape icon}]] - [:div.item-info {:on-click ignore-click} - (if edition? - [:input.element-name {:type "text" - :auto-focus true - :on-key-down on-key-down - :on-blur on-blur - :on-click on-edit - :default-value (:name icon)}] - [:h3 {:on-double-click on-edit} - (:name icon)]) - (str (tr "ds.uploaded-at" (dt/format (:created-at icon) "dd/MM/yyyy")))]])) - -;; --- Grid - -(def icons-iref - (-> (comp (l/key :icons) (l/lens vals)) - (l/derive st/state))) - -(mf/defc grid - [{:keys [id type collection opts] :as props}] - (let [editable? (= type :own) - icons (->> (mf/deref icons-iref) - (filter-icons-by (:filter opts "")) - (sort-icons-by (:order opts :name)))] - [:div.dashboard-grid-content - [:div.dashboard-grid-row - (when editable? - [:& grid-form {:id id :type type :uploading? (:uploading opts)}]) - - [:& chunked-list {:items icons - :initial-size 30 - :chunk-size 30 - :key (str type id (count icons))} - (fn [icon] - [:& grid-item {:icon icon - :key (:id icon) - :selected (contains? (:selected opts) (:id icon)) - :edition? (= (:edition opts) (:id icon))}])]]])) - -;; --- Content - -(def opts-iref - (-> (l/key :dashboard-icons) - (l/derive st/state))) - -(mf/defc content - [{:keys [id type collection] :as props}] - (let [{:keys [selected] :as opts} (mf/deref opts-iref)] - [:section.dashboard-grid.library - (when collection - [:& grid-header {:collection collection}]) - (if collection - [:& grid {:id id :type type :collection collection :opts opts}] - [:span "EMPTY STATE TODO"]) - (when-not (empty? selected) - #_[:& grid-options {:id id :type type :selected (:selected opts)}])])) - -;; --- Icons Page - -(def collections-iref - (-> (l/key :icons-collections) - (l/derive st/state))) - -(mf/defc icons-page - [{:keys [id type] :as props}] - (let [type (or type :own) - collections (mf/deref collections-iref) - collections (cond->> (vals collections) - (= type :own) (filter #(= :own (:type %))) - (= type :builtin) (filter #(= :builtin (:type %))) - true (sort-by :created-at)) - - collection (cond - (uuid? id) (seek #(= id (:id %)) collections) - :else (first collections)) - - id (:id collection)] - - (mf/use-effect #(st/emit! di/fetch-collections)) - (mf/use-effect - {:fn #(when id (st/emit! (di/initialize id))) - :deps (mf/deps id)}) - - [:section.dashboard-content - [:& nav {:type type :id id :collections collections}] - [:& content {:type type :id id :collection collection}]])) +;; ;; --- Helpers & Constants +;; +;; (def +ordering-options+ +;; {:name "ds.ordering.by-name" +;; :created "ds.ordering.by-creation-date"}) +;; +;; (defn- sort-icons-by +;; [ordering icons] +;; (case ordering +;; :name (sort-by :name icons) +;; :created (reverse (sort-by :created-at icons)) +;; icons)) +;; +;; (defn- contains-term? +;; [phrase term] +;; {:pre [(string? phrase) +;; (string? term)]} +;; (let [term (name term)] +;; (str/includes? (str/lower phrase) (str/trim (str/lower term))))) +;; +;; (defn- filter-icons-by +;; [term icons] +;; (if (str/blank? term) +;; icons +;; (filter #(contains-term? (:name %) term) icons))) +;; +;; ;; --- Component: Grid Header +;; +;; (mf/defc grid-header +;; [{:keys [collection] :as props}] +;; (let [{:keys [id type]} collection +;; on-change #(st/emit! (di/rename-collection id %)) +;; on-deleted #(st/emit! (rt/nav :dashboard-icons nil {:type type})) +;; delete #(st/emit! (di/delete-collection id on-deleted)) +;; on-delete #(modal/show! confirm-dialog {:on-accept delete})] +;; [:& common/grid-header {:value (:name collection) +;; :on-change on-change +;; :on-delete on-delete}])) +;; +;; ;; --- Nav +;; +;; (mf/defc nav-item +;; [{:keys [collection selected?] :as props}] +;; (let [local (mf/use-state {}) +;; {:keys [id type name]} collection +;; editable? (= type :own) +;; +;; on-click +;; (fn [event] +;; (let [type (or type :own)] +;; (st/emit! (rt/nav :dashboard-icons {} {:type type :id id})))) +;; +;; +;; on-input-change +;; (fn [event] +;; (-> (dom/get-target event) +;; (dom/get-value) +;; (swap! local assoc :name))) +;; +;; on-cancel #(swap! local dissoc :name :edit) +;; on-double-click #(when editable? (swap! local assoc :edit true)) +;; +;; on-input-keyup +;; (fn [event] +;; (when (kbd/enter? event) +;; (let [value (-> (dom/get-target event) (dom/get-value))] +;; (st/emit! (di/rename-collection id (str/trim (:name @local)))) +;; (swap! local assoc :edit false))))] +;; +;; [:li {:on-click on-click +;; :on-double-click on-double-click +;; :class-name (when selected? "current")} +;; (if (:edit @local) +;; [:div +;; [:input.element-title {:value (or (:name @local) name) +;; :on-change on-input-change +;; :on-key-down on-input-keyup}] +;; [:span.close {:on-click on-cancel} i/close]] +;; [:span.element-title name])])) +;; +;; +;; (mf/defc nav +;; [{:keys [id type collections] :as props}] +;; (let [locale (i18n/use-locale) +;; own? (= type :own) +;; builtin? (= type :builtin) +;; create-collection #(st/emit! di/create-collection) +;; select-own-tab #(st/emit! (rt/nav :dashboard-icons nil {:type :own})) +;; select-buitin-tab #(st/emit! (rt/nav :dashboard-icons nil {:type :builtin}))] +;; +;; [:div.library-bar +;; [:div.library-bar-inside +;; ;; Tabs +;; [:ul.library-tabs +;; [:li {:class (when own? "current") +;; :on-click select-own-tab} +;; (t locale "ds.your-icons-title")] +;; +;; [:li {:class (when builtin? "current") +;; :on-click select-buitin-tab} +;; (t locale "ds.store-icons-title")]] +;; +;; +;; ;; Collections List +;; [:ul.library-elements +;; (when own? +;; [:li +;; [:a.btn-primary {:on-click #(st/emit! di/create-collection)} +;; (tr "ds.icons-collection.new")]]) +;; (for [item collections] +;; [:& nav-item {:collection item +;; :selected? (= (:id item) id) +;; :key (:id item)}])]]])) +;; +;; +;; ;; (mf/def grid-options-tooltip +;; ;; :mixins [mf/reactive mf/memo] +;; +;; ;; :render +;; ;; (fn [own {:keys [selected on-select title]}] +;; ;; {:pre [(uuid? selected) +;; ;; (fn? on-select) +;; ;; (string? title)]} +;; ;; (let [colls (mf/react collections-iref) +;; ;; colls (->> (vals colls) +;; ;; (filter #(= :own (:type %))) +;; ;; (remove #(= selected (:id %))) +;; ;; (sort-by :name colls)) +;; ;; on-select (fn [event id] +;; ;; (dom/prevent-default event) +;; ;; (dom/stop-propagation event) +;; ;; (on-select id))] +;; ;; [:ul.move-list +;; ;; [:li.title title] +;; ;; [:li +;; ;; [:a {:href "#" :on-click #(on-select % nil)} "Storage"]] +;; ;; (for [{:keys [id name] :as coll} colls] +;; ;; [:li {:key (pr-str id)} +;; ;; [:a {:on-click #(on-select % id)} name]])]))) +;; +;; (mf/defc grid-options +;; [{:keys [id type selected] :as props}] +;; (let [local (mf/use-state {}) +;; delete #(st/emit! di/delete-selected) +;; on-delete #(modal/show! confirm-dialog {:on-accept delete}) +;; +;; ;; (on-toggle-copy [event] +;; ;; (swap! local update :show-copy-tooltip not)) +;; ;; (on-toggle-move [event] +;; ;; (swap! local update :show-move-tooltip not)) +;; ;; (on-copy [selected] +;; ;; (swap! local assoc +;; ;; :show-move-tooltip false +;; ;; :show-copy-tooltip false) +;; ;; (st/emit! (di/copy-selected selected))) +;; ;; (on-move [selected] +;; ;; (swap! local assoc +;; ;; :show-move-tooltip false +;; ;; :show-copy-tooltip false) +;; ;; (st/emit! (di/move-selected selected))) +;; ;; (on-rename [event] +;; ;; (let [selected (first selected)] +;; ;; (st/emit! (di/update-opts :edition selected)))) +;; ] +;; ;; MULTISELECT OPTIONS BAR +;; [:div.multiselect-bar +;; (when (= type :own) +;; ;; If editable +;; [:div.multiselect-nav +;; ;; [:span.move-item.tooltip.tooltip-top +;; ;; {:alt (tr "ds.multiselect-bar.copy") +;; ;; :on-click on-toggle-copy} +;; ;; (when (:show-copy-tooltip @local) +;; ;; [:& grid-options-tooltip {:selected id +;; ;; :title (tr "ds.multiselect-bar.copy-to-library") +;; ;; :on-select on-copy}]) +;; ;; i/copy] +;; ;; [:span.move-item.tooltip.tooltip-top +;; ;; {:alt (tr "ds.multiselect-bar.move") +;; ;; :on-click on-toggle-move} +;; ;; (when (:show-move-tooltip @local) +;; ;; [:& grid-options-tooltip {:selected id +;; ;; :title (tr "ds.multiselect-bar.move-to-library") +;; ;; :on-select on-move}]) +;; ;; i/move] +;; ;; (when (= 1 (count selected)) +;; ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.rename") +;; ;; :on-click on-rename} +;; ;; i/pencil]) +;; [:span.delete.tooltip.tooltip-top +;; {:alt (tr "ds.multiselect-bar.delete") +;; :on-click on-delete} +;; i/trash]] +;; +;; ;; If not editable +;; ;; [:div.multiselect-nav +;; ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.copy") +;; ;; :on-click on-toggle-copy} +;; ;; (when (:show-copy-tooltip @local) +;; ;; [:& grid-options-tooltip {:selected id +;; ;; :title (tr "ds.multiselect-bar.copy-to-library") +;; ;; :on-select on-copy}]) +;; ;; i/organize]] +;; )])) +;; +;; ;; --- Grid Form +;; +;; (mf/defc grid-form +;; [{:keys [id type uploading?] :as props}] +;; (let [locale (i18n/use-locale) +;; input (mf/use-ref nil) +;; on-click #(dom/click (mf/ref-node input)) +;; on-select #(st/emit! (->> (dom/get-target %) +;; (dom/get-files) +;; (array-seq) +;; (di/create-icons id)))] +;; [:div.grid-item.add-project {:on-click on-click} +;; (if uploading? +;; [:div i/loader-pencil] +;; [:span (t locale "ds.icon-new")]) +;; [:input.upload-icon-input +;; {:style {:display "none"} +;; :multiple true +;; :ref input +;; :value "" +;; :accept "icon/svg+xml" +;; :type "file" +;; :on-change on-select}]])) +;; +;; ;; --- Grid Item +;; +;; (mf/defc grid-item +;; [{:keys [icon selected? edition?] :as props}] +;; (let [toggle-selection #(st/emit! (if selected? +;; (di/deselect-icon (:id icon)) +;; (di/select-icon (:id icon)))) +;; on-blur +;; (fn [event] +;; (let [target (dom/get-target event) +;; name (dom/get-value target)] +;; (st/emit! (di/update-opts :edition false) +;; (di/rename-icon (:id icon) name)))) +;; +;; on-key-down +;; (fn [event] +;; (when (kbd/enter? event) +;; (on-blur event))) +;; +;; ignore-click +;; (fn [event] +;; (dom/stop-propagation event) +;; (dom/prevent-default event)) +;; +;; on-edit +;; (fn [event] +;; (dom/stop-propagation event) +;; (dom/prevent-default event) +;; (st/emit! (di/update-opts :edition (:id icon))))] +;; +;; [:div.grid-item.small-item.project-th +;; [:div.input-checkbox.check-primary +;; [:input {:type "checkbox" +;; :id (:id icon) +;; :on-change toggle-selection +;; :checked selected?}] +;; [:label {:for (:id icon)}]] +;; [:span.grid-item-icon +;; [:& icon/icon-svg {:shape icon}]] +;; [:div.item-info {:on-click ignore-click} +;; (if edition? +;; [:input.element-name {:type "text" +;; :auto-focus true +;; :on-key-down on-key-down +;; :on-blur on-blur +;; :on-click on-edit +;; :default-value (:name icon)}] +;; [:h3 {:on-double-click on-edit} +;; (:name icon)]) +;; (str (tr "ds.uploaded-at" (dt/format (:created-at icon) "dd/MM/yyyy")))]])) +;; +;; ;; --- Grid +;; +;; (def icons-iref +;; (-> (comp (l/key :icons) (l/lens vals)) +;; (l/derive st/state))) +;; +;; (mf/defc grid +;; [{:keys [id type collection opts] :as props}] +;; (let [editable? (= type :own) +;; icons (->> (mf/deref icons-iref) +;; (filter-icons-by (:filter opts "")) +;; (sort-icons-by (:order opts :name)))] +;; [:div.dashboard-grid-content +;; [:div.dashboard-grid-row +;; (when editable? +;; [:& grid-form {:id id :type type :uploading? (:uploading opts)}]) +;; +;; [:& chunked-list {:items icons +;; :initial-size 30 +;; :chunk-size 30 +;; :key (str type id (count icons))} +;; (fn [icon] +;; [:& grid-item {:icon icon +;; :key (:id icon) +;; :selected (contains? (:selected opts) (:id icon)) +;; :edition? (= (:edition opts) (:id icon))}])]]])) +;; +;; ;; --- Content +;; +;; (def opts-iref +;; (-> (l/key :dashboard-icons) +;; (l/derive st/state))) +;; +;; (mf/defc content +;; [{:keys [id type collection] :as props}] +;; (let [{:keys [selected] :as opts} (mf/deref opts-iref)] +;; [:section.dashboard-grid.library +;; (when collection +;; [:& grid-header {:collection collection}]) +;; (if collection +;; [:& grid {:id id :type type :collection collection :opts opts}] +;; [:span "EMPTY STATE TODO"]) +;; (when-not (empty? selected) +;; #_[:& grid-options {:id id :type type :selected (:selected opts)}])])) +;; +;; ;; --- Icons Page +;; +;; (def collections-iref +;; (-> (l/key :icons-collections) +;; (l/derive st/state))) +;; +;; (mf/defc icons-page +;; [{:keys [id type] :as props}] +;; (let [type (or type :own) +;; collections (mf/deref collections-iref) +;; collections (cond->> (vals collections) +;; (= type :own) (filter #(= :own (:type %))) +;; (= type :builtin) (filter #(= :builtin (:type %))) +;; true (sort-by :created-at)) +;; +;; collection (cond +;; (uuid? id) (seek #(= id (:id %)) collections) +;; :else (first collections)) +;; +;; id (:id collection)] +;; +;; (mf/use-effect #(st/emit! di/fetch-collections)) +;; (mf/use-effect +;; {:fn #(when id (st/emit! (di/initialize id))) +;; :deps (mf/deps id)}) +;; +;; [:section.dashboard-content +;; [:& nav {:type type :id id :collections collections}] +;; [:& content {:type type :id id :collection collection}]])) diff --git a/frontend/src/uxbox/main/ui/dashboard/library.cljs b/frontend/src/uxbox/main/ui/dashboard/library.cljs index 5cad30fec0..da4f9eaf37 100644 --- a/frontend/src/uxbox/main/ui/dashboard/library.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/library.cljs @@ -12,125 +12,196 @@ (:require [lentes.core :as l] [rumext.alpha :as mf] + [cuerdas.core :as str] + [uxbox.util.router :as rt] + [uxbox.util.i18n :as i18n :refer [t tr]] + [uxbox.util.color :as uc] + [uxbox.main.data.icons :as dico] + [uxbox.main.data.images :as dimg] + [uxbox.main.data.colors :as dcol] [uxbox.builtins.icons :as i] [uxbox.main.store :as st] [uxbox.main.refs :as refs] - [uxbox.util.i18n :as i18n :refer [t tr]] [uxbox.main.ui.dashboard.components.context-menu :refer [context-menu]])) (mf/defc library-header - [{:keys [profile] :as props}] - (let [locale (i18n/use-locale)] + [{:keys [section team-id] :as props}] + (let [icons? (= section :icons) + images? (= section :images) + palettes? (= section :palettes) + locale (i18n/use-locale)] [:header#main-bar.main-bar [:h1.dashboard-title "Libraries"] [:nav.library-header-navigation - [:a.library-header-navigation-item "Icons"] - [:a.library-header-navigation-item "Images"] - [:a.library-header-navigation-item "Colors"]]])) + [:a.library-header-navigation-item + {:class-name (when icons? "current") + :on-click #(st/emit! (rt/nav :dashboard-library-icons-index {:team-id team-id}))} + (t locale "dashboard.library.menu.icons")] + [:a.library-header-navigation-item + {:class-name (when images? "current") + :on-click #(st/emit! (rt/nav :dashboard-library-images-index {:team-id team-id}))} + (t locale "dashboard.library.menu.images")] + [:a.library-header-navigation-item + {:class-name (when palettes? "current") + :on-click #(st/emit! (rt/nav :dashboard-library-palettes-index {:team-id team-id}))} + (t locale "dashboard.library.menu.palettes")]]])) (mf/defc library-sidebar - [] - [:aside.library-sidebar - [:button.library-sidebar-add-item - {:type "button"} - "+ New icon library"] - [:ul.library-sidebar-list - [:li.library-sidebar-list-element [:a "Ecometer"]] - [:li.library-sidebar-list-element [:a "Tipi"]] - [:li.library-sidebar-list-element [:a "Taiga (inspirational)"]] - [:li.library-sidebar-list-element [:a "DKT photo assets"]]]]) + [{:keys [section items team-id library-id]}] + (let [locale (i18n/use-locale)] + [:aside.library-sidebar + [:button.library-sidebar-add-item + {:type "button"} + (t locale (str "dashboard.library.add-library." (name section)))] + [:ul.library-sidebar-list + (for [item items] + [:li.library-sidebar-list-element + {:key (:id item) + :class-name (when (= library-id (:id item)) "current") + :on-click (fn [] (let [path (keyword (str "dashboard-library-" (name section))) + route (rt/nav path {:team-id team-id + :library-id (:id item)})] + (dico/fetch-icon-library (:id item)) + (st/emit! route)))} + [:a (:name item)]])]])) (mf/defc library-top-menu - [] - (let [state (mf/use-state {:is-open false})] + [{:keys [selected section]}] + (let [state (mf/use-state {:is-open false}) + locale (i18n/use-locale)] [:header.library-top-menu [:div.library-top-menu-current-element - [:h2.library-top-menu-current-element-name "UXBOX"] + [:h2.library-top-menu-current-element-name (:name selected)] [:a.library-top-menu-current-action { :on-click #(swap! state update :is-open not)} [:span i/arrow-down]] [:& context-menu {:is-open (:is-open @state) - :options [["Rename" #(println "Rename")] - ["Delete" #(println "Delete")]]}]] + :options [[(t locale "ds.button.rename") #(println "Rename")] + [(t locale "ds.button.delete") #(println "Delete")]]}]] [:div.library-top-menu-actions [:a i/trash] - [:a.btn-dashboard "+ New icon"]]])) + [:a.btn-dashboard (t locale (str "dashboard.library.add-item." (name section)))]]])) -(mf/defc library-icon-card [] - (let [state (mf/use-state {:is-open false})] +(mf/defc library-icon-card + [{:keys [id name url content metadata]}] + (let [locale (i18n/use-locale) + state (mf/use-state {:is-open false})] [:div.library-card.library-icon [:div.input-checkbox.check-primary [:input {:type "checkbox" - :id "card" + :id (str "icon-" id) :on-change #(println "toggle-selection") #_(:checked false)}] - [:label {:for "card"}]] - [:div.library-card-image i/trash] + [:label {:for (str "icon-" id)}]] + [:div.library-card-image + #_[:object { :data url :type "image/svg+xml" }] + [:svg {:view-box (->> metadata :view-box (str/join " ")) + :width (:width metadata) + :height (:height metadata) + :dangerouslySetInnerHTML {:__html content}}]] + [:div.library-card-footer - [:div.library-card-footer-name "my-dear-icon.svg"] + [:div.library-card-footer-name name] [:div.library-card-footer-timestamp "Less than 5 seconds ago"] [:div.library-card-footer-menu { :on-click #(swap! state update :is-open not) } i/actions] [:& context-menu {:is-open (:is-open @state) - :options [["Delete" #(println "Delete")]]}]]])) + :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) -(mf/defc library-image-card [] - (let [state (mf/use-state {:is-open false})] +(mf/defc library-image-card + [{:keys [id name url]}] + (let [locale (i18n/use-locale) + state (mf/use-state {:is-open false})] [:div.library-card.library-image [:div.input-checkbox.check-primary [:input {:type "checkbox" - :id "card" + :id (str "image-" id) :on-change #(println "toggle-selection") #_(:checked false)}] - [:label {:for "card"}]] + [:label {:for (str "image-" id)}]] [:div.library-card-image - [:img {:src "https://www.placecage.com/200/200"}]] + [:img {:src url}]] [:div.library-card-footer - [:div.library-card-footer-name "my-dear-icon.svg"] + [:div.library-card-footer-name name] [:div.library-card-footer-timestamp "Less than 5 seconds ago"] [:div.library-card-footer-menu { :on-click #(swap! state update :is-open not) } i/actions] [:& context-menu {:is-open (:is-open @state) - :options [["Delete" #(println "Delete")]]}]]])) + :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) -(mf/defc library-color-card [] - (let [state (mf/use-state {:is-open false})] +(mf/defc library-color-card + [{ :keys [ id content ] }] + (let [locale (i18n/use-locale) + state (mf/use-state {:is-open false})] [:div.library-card.library-color [:div.input-checkbox.check-primary [:input {:type "checkbox" - :id "card" + :id (str "color-" id) :on-change #(println "toggle-selection") #_(:checked false)}] - [:label {:for "card"}]] + [:label {:for (str "color-" id)}]] [:div.library-card-image - { :style { :background-color "#9B78FF" }}] + { :style { :background-color content }}] [:div.library-card-footer - #_[:* - [:div.library-card-footer-name "my-dear-icon.svg"] - [:div.library-card-footer-timestamp "Less than 5 seconds ago"]] - [:* - [:div.library-card-footer-name "#9B78FF"] - [:div.library-card-footer-color - [:span.library-card-footer-color-label "RGB"] - [:span.library-card-footer-color-rgb "128, 128, 128"]]] + [:div.library-card-footer-name content ] + [:div.library-card-footer-color + [:span.library-card-footer-color-label "RGB"] + [:span.library-card-footer-color-rgb (str/join " " (uc/hex->rgb content))]] [:div.library-card-footer-menu { :on-click #(swap! state update :is-open not) } i/actions] [:& context-menu {:is-open (:is-open @state) - :options [["Delete" #(println "Delete")]]}]]])) + :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) + +(def icon-libraries-ref + (-> (comp (l/key :library) (l/key :icon-libraries)) + (l/derive st/state))) + +(def image-libraries-ref + (-> (comp (l/key :library) (l/key :image-libraries)) + (l/derive st/state))) + +(def color-libraries-ref + (-> (comp (l/key :library) (l/key :color-libraries)) + (l/derive st/state))) + +(def selected-items-ref + (-> (comp (l/key :library) (l/key :selected-items)) + (l/derive st/state))) (mf/defc library-page - [{:keys [team-id]}] - [:div.library-page - [:& library-header] - [:& library-sidebar] - [:section.library-content - [:& library-top-menu] - [:div.library-page-cards-container - (for [_ (range 0 10)] - #_[:& library-icon-card] - #_[:& library-image-card] - [:& library-color-card])]]]) + [{:keys [team-id library-id section]}] + (mf/use-effect {:fn #(case section + :icons (st/emit! (dico/fetch-icon-libraries team-id)) + :images (st/emit! (dimg/fetch-image-libraries team-id)) + :palettes (st/emit! (dcol/fetch-color-libraries team-id))) + :deps (mf/deps section team-id)}) + (mf/use-effect {:fn #(when library-id + (case section + :icons (st/emit! (dico/fetch-icon-library library-id)) + :images (st/emit! (dimg/fetch-image-library library-id)) + :palettes (st/emit! (dcol/fetch-color-library library-id)))) + :deps (mf/deps library-id)}) + (let [libraries (case section + :icons (mf/deref icon-libraries-ref) + :images (mf/deref image-libraries-ref) + :palettes (mf/deref color-libraries-ref)) + items (mf/deref selected-items-ref) + selected-library (first (filter #(= (:id %) library-id) libraries))] + [:div.library-page + [:& library-header {:section section :team-id team-id}] + [:& library-sidebar {:items libraries :team-id team-id :library-id library-id :section section}] + + (when library-id + [:section.library-content + [:& library-top-menu {:selected selected-library :section section}] + [:div.library-page-cards-container + (for [item items] + (let [item (assoc item :key (:id item))] + (case section + :icons [:& library-icon-card item] + :images [:& library-image-card { :name "Nicolas Cage" :url "https://www.placecage.com/200/200" }] + :palettes [:& library-color-card item ])))]])])) diff --git a/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs b/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs index 5a28e755d9..58fbc58702 100644 --- a/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/sidebar.cljs @@ -95,7 +95,7 @@ drafts? (and (= selected-section :dashboard-project) (= selected-team-id (:default-team-id profile)) (= selected-project-id (:default-project-id profile))) - library? (and (= selected-section :dashboard-library) + library? (and (str/starts-with? (name selected-section) "dashboard-library") (= selected-team-id (:default-team-id profile))) locale (i18n/use-locale)] [:ul.library-elements @@ -114,7 +114,7 @@ [:li - {:on-click #(st/emit! (rt/nav :dashboard-library {:team-id team-id})) + {:on-click #(st/emit! (rt/nav :dashboard-library-icons-index {:team-id team-id})) :class-name (when library? "current")} i/icon-set [:span.element-title (t locale "dashboard.sidebar.libraries")]] diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs index 1b9b474a23..1a843f4abc 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs @@ -15,7 +15,7 @@ [uxbox.main.data.workspace :as dw] [uxbox.main.refs :as refs] [uxbox.main.store :as st] - [uxbox.main.ui.dashboard.icons :as icons] + #_[uxbox.main.ui.dashboard.icons :as icons] [uxbox.main.ui.shapes.icon :as icon] [uxbox.util.data :refer [read-string]] [uxbox.util.dom :as dom] @@ -40,7 +40,7 @@ (mf/defc icons-list [{:keys [collection-id] :as props}] - (let [icons (mf/deref icons/icons-iref) + (let [icons [] #_(mf/deref icons/icons-iref) ;; TODO: Fix this on-select (fn [event data] @@ -67,7 +67,7 @@ local (mf/deref refs/workspace-local) - collections (vals (mf/deref icons/collections-iref)) + collections (vals [] #_(mf/deref icons/collections-iref)) ;; TODO: FIX THIS collection (first collections) on-close From 5b7182fac6b2aef5133116a9e3cbb3875f2883ea Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 Mar 2020 12:22:21 +0100 Subject: [PATCH 3/3] :snowflake: integration with new items --- frontend/resources/locales.json | 5 +- frontend/resources/styles/common/base.scss | 2 +- .../styles/main/layouts/library-page.scss | 50 ++ frontend/src/uxbox/main/data/colors.cljs | 488 ++++++------ frontend/src/uxbox/main/data/icons.cljs | 254 ++++--- frontend/src/uxbox/main/data/images.cljs | 617 +++++++-------- frontend/src/uxbox/main/ui/colorpicker.cljs | 20 +- .../src/uxbox/main/ui/dashboard/colors.cljs | 538 ++++++------- .../ui/dashboard/components/context_menu.cljs | 15 - .../src/uxbox/main/ui/dashboard/images.cljs | 714 +++++++++--------- .../src/uxbox/main/ui/dashboard/library.cljs | 168 ++++- .../uxbox/main/ui/workspace/colorpalette.cljs | 2 +- .../src/uxbox/main/ui/workspace/images.cljs | 4 +- .../main/ui/workspace/sidebar/icons.cljs | 4 +- 14 files changed, 1549 insertions(+), 1332 deletions(-) delete mode 100644 frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs diff --git a/frontend/resources/locales.json b/frontend/resources/locales.json index b1247c6058..084cf910f4 100644 --- a/frontend/resources/locales.json +++ b/frontend/resources/locales.json @@ -1125,5 +1125,8 @@ "translations" : { "en" : "Click to close the path" } - } + }, + + "modal.create-color.new-color": "New Color", + "modal.create-color.edit-color": "Edit Color" } diff --git a/frontend/resources/styles/common/base.scss b/frontend/resources/styles/common/base.scss index b33b94a0a9..e42defc32b 100644 --- a/frontend/resources/styles/common/base.scss +++ b/frontend/resources/styles/common/base.scss @@ -18,7 +18,7 @@ body { * { box-sizing: border-box; - transition: all .4s ease; + // transition: all .4s ease; } .global-zeroclipboard-container { diff --git a/frontend/resources/styles/main/layouts/library-page.scss b/frontend/resources/styles/main/layouts/library-page.scss index 2bc0746f69..3ba00eb4b2 100644 --- a/frontend/resources/styles/main/layouts/library-page.scss +++ b/frontend/resources/styles/main/layouts/library-page.scss @@ -285,3 +285,53 @@ top: 1.5rem; } } + + +.modal-create-color { + position: relative; + background-color: $color-white; + padding: 4rem; + display: flex; + flex-direction: column; + align-items: center; + + & .sketch-picker { + box-shadow: none !important; + border: 1px solid $color-gray-lighter !important; + border-radius: 0 !important; + + & input { + background-color: $color-white; + } + } + + & .close { + position: absolute; + right: 1rem; + transform: rotate(45deg); + top: 1rem; + + svg { + fill: $color-black; + height: 20px; + width: 20px; + + &:hover { + fill: $color-danger; + } + + } + } + + & .btn-primary { + width: 10rem; + padding: 0.5rem; + margin-top: 1rem; + } +} + +.modal-create-color-title { + color: $color-black; + font-size: 24px; + font-weight: normal; +} diff --git a/frontend/src/uxbox/main/data/colors.cljs b/frontend/src/uxbox/main/data/colors.cljs index cdfed29bd2..4291bd61d9 100644 --- a/frontend/src/uxbox/main/data/colors.cljs +++ b/frontend/src/uxbox/main/data/colors.cljs @@ -19,230 +19,230 @@ [uxbox.util.time :as dt] [uxbox.util.uuid :as uuid])) -;; TODO: need a good refactor - -;; --- Initialize - -(declare fetch-collections) -(declare persist-collections) -(declare collections-fetched?) - -;; --- Collections Fetched - -(defrecord CollectionsFetched [data] - ptk/UpdateEvent - (update [_ state] - (let [{:keys [version value]} data] - (-> state - (update :colors-collections merge value) - (assoc ::version version))))) - -(defn collections-fetched - [data] - {:pre [(map? data)]} - (CollectionsFetched. data)) - -(defn collections-fetched? - [v] - (instance? CollectionsFetched v)) - -;; --- Fetch Collections - -(defrecord FetchCollections [] - ptk/WatchEvent - (watch [_ state s] - (->> (rp/query! :user-attr {:key "color-collections"}) - (rx/map collections-fetched) - (rx/catch (fn [{:keys [type] :as error}] - (if (= type :not-found) - (rx/empty) - (rx/throw error))))))) - -(defn fetch-collections - [] - (FetchCollections.)) - -;; --- Create Collection - -(defrecord CreateCollection [id] - ptk/UpdateEvent - (update [_ state] - (let [item {:name (tr "ds.default-library-title" (gensym "c")) - :id id - :created-at (dt/now) - :type :own - :colors #{}}] - (assoc-in state [:colors-collections id] item))) - - ptk/WatchEvent - (watch [_ state stream] - (rx/of (persist-collections) - (rt/nav :dashboard/colors nil {:type :own :id id})))) - -(defn create-collection - [] - (let [id (uuid/next)] - (CreateCollection. id))) - -;; --- Persist Collections - -(defrecord PersistCollections [] - ptk/WatchEvent - (watch [_ state stream] - (let [builtin? #(= :builtin (:type %)) - xform (remove (comp builtin? second)) - version (or (get state ::version) -1) - value (->> (get state :colors-collections) - (into {} xform)) - data {:key "color-collections" - :val value}] - (->> (rp/mutation! :upsert-user-attr data) - (rx/map collections-fetched))))) - -(defn persist-collections - [] - (PersistCollections.)) - -;; --- Rename Collection - -(defrecord RenameCollection [id name] - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:colors-collections id :name] name)) - - ptk/WatchEvent - (watch [_ state s] - (rx/of (persist-collections)))) - -(defn rename-collection - [item name] - (RenameCollection. item name)) - -;; --- Delete Collection - -(defrecord DeleteCollection [id] - ptk/UpdateEvent - (update [_ state] - (update state :colors-collections dissoc id)) - - ptk/WatchEvent - (watch [_ state s] - (rx/of (persist-collections)))) - -(defn delete-collection - [id] - (DeleteCollection. id)) - -;; --- Replace Color - -(defrecord AddColor [coll-id color] - ptk/UpdateEvent - (update [_ state] - (update-in state [:colors-collections coll-id :colors] set/union #{color})) - - ptk/WatchEvent - (watch [_ state s] - (rx/of (persist-collections)))) - -(defn add-color - "Add or replace color in a collection." - [coll-id color] - (AddColor. coll-id color)) - -;; --- Remove Color - -(defrecord RemoveColors [id colors] - ptk/UpdateEvent - (update [_ state] - (update-in state [:colors-collections id :colors] - #(set/difference % colors))) - - ptk/WatchEvent - (watch [_ state s] - (rx/of (persist-collections)))) - -(defn remove-colors - "Remove color in a collection." - [id colors] - (RemoveColors. id colors)) - -;; --- Select color - -(defrecord SelectColor [color] - ptk/UpdateEvent - (update [_ state] - (update-in state [:dashboard :colors :selected] conj color))) - -(defrecord DeselectColor [color] - ptk/UpdateEvent - (update [_ state] - (update-in state [:dashboard :colors :selected] disj color))) - -(defrecord ToggleColorSelection [color] - ptk/WatchEvent - (watch [_ state stream] - (let [selected (get-in state [:dashboard :colors :selected])] - (rx/of - (if (selected color) - (DeselectColor. color) - (SelectColor. color)))))) - -(defn toggle-color-selection - [color] - {:pre [(color/hex? color)]} - (ToggleColorSelection. color)) - -;; --- Copy Selected Color - -(defrecord CopySelected [id] - ptk/UpdateEvent - (update [_ state] - (let [selected (get-in state [:dashboard :colors :selected])] - (update-in state [:colors-collections id :colors] set/union selected))) - - ptk/WatchEvent - (watch [_ state stream] - (rx/of (persist-collections)))) - -(defn copy-selected - [id] - {:pre [(or (uuid? id) (nil? id))]} - (CopySelected. id)) - -;; --- Move Selected Color - -(defrecord MoveSelected [from to] - ptk/UpdateEvent - (update [_ state] - (let [selected (get-in state [:dashboard :colors :selected])] - (-> state - (update-in [:colors-collections from :colors] set/difference selected) - (update-in [:colors-collections to :colors] set/union selected)))) - - ptk/WatchEvent - (watch [_ state stream] - (rx/of (persist-collections)))) - -(defn move-selected - [from to] - {:pre [(or (uuid? from) (nil? from)) - (or (uuid? to) (nil? to))]} - (MoveSelected. from to)) - -;; --- Delete Colors - -(defrecord DeleteColors [coll-id colors] - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:dashboard :colors :selected] #{})) - - ptk/WatchEvent - (watch [_ state stream] - (rx/of (remove-colors coll-id colors)))) - -(defn delete-colors - [coll-id colors] - (DeleteColors. coll-id colors)) +;; ;; TODO: need a good refactor +;; +;; ;; --- Initialize +;; +;; (declare fetch-collections) +;; (declare persist-collections) +;; (declare collections-fetched?) +;; +;; ;; --- Collections Fetched +;; +;; (defrecord CollectionsFetched [data] +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [{:keys [version value]} data] +;; (-> state +;; (update :colors-collections merge value) +;; (assoc ::version version))))) +;; +;; (defn collections-fetched +;; [data] +;; {:pre [(map? data)]} +;; (CollectionsFetched. data)) +;; +;; (defn collections-fetched? +;; [v] +;; (instance? CollectionsFetched v)) +;; +;; ;; --- Fetch Collections +;; +;; (defrecord FetchCollections [] +;; ptk/WatchEvent +;; (watch [_ state s] +;; (->> (rp/query! :user-attr {:key "color-collections"}) +;; (rx/map collections-fetched) +;; (rx/catch (fn [{:keys [type] :as error}] +;; (if (= type :not-found) +;; (rx/empty) +;; (rx/throw error))))))) +;; +;; (defn fetch-collections +;; [] +;; (FetchCollections.)) +;; +;; ;; --- Create Collection +;; +;; (defrecord CreateCollection [id] +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [item {:name (tr "ds.default-library-title" (gensym "c")) +;; :id id +;; :created-at (dt/now) +;; :type :own +;; :colors #{}}] +;; (assoc-in state [:colors-collections id] item))) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (persist-collections) +;; (rt/nav :dashboard/colors nil {:type :own :id id})))) +;; +;; (defn create-collection +;; [] +;; (let [id (uuid/next)] +;; (CreateCollection. id))) +;; +;; ;; --- Persist Collections +;; +;; (defrecord PersistCollections [] +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (let [builtin? #(= :builtin (:type %)) +;; xform (remove (comp builtin? second)) +;; version (or (get state ::version) -1) +;; value (->> (get state :colors-collections) +;; (into {} xform)) +;; data {:key "color-collections" +;; :val value}] +;; (->> (rp/mutation! :upsert-user-attr data) +;; (rx/map collections-fetched))))) +;; +;; (defn persist-collections +;; [] +;; (PersistCollections.)) +;; +;; ;; --- Rename Collection +;; +;; (defrecord RenameCollection [id name] +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:colors-collections id :name] name)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (rx/of (persist-collections)))) +;; +;; (defn rename-collection +;; [item name] +;; (RenameCollection. item name)) +;; +;; ;; --- Delete Collection +;; +;; (defrecord DeleteCollection [id] +;; ptk/UpdateEvent +;; (update [_ state] +;; (update state :colors-collections dissoc id)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (rx/of (persist-collections)))) +;; +;; (defn delete-collection +;; [id] +;; (DeleteCollection. id)) +;; +;; ;; --- Replace Color +;; +;; (defrecord AddColor [coll-id color] +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:colors-collections coll-id :colors] set/union #{color})) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (rx/of (persist-collections)))) +;; +;; (defn add-color +;; "Add or replace color in a collection." +;; [coll-id color] +;; (AddColor. coll-id color)) +;; +;; ;; --- Remove Color +;; +;; (defrecord RemoveColors [id colors] +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:colors-collections id :colors] +;; #(set/difference % colors))) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (rx/of (persist-collections)))) +;; +;; (defn remove-colors +;; "Remove color in a collection." +;; [id colors] +;; (RemoveColors. id colors)) +;; +;; ;; --- Select color +;; +;; (defrecord SelectColor [color] +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:dashboard :colors :selected] conj color))) +;; +;; (defrecord DeselectColor [color] +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:dashboard :colors :selected] disj color))) +;; +;; (defrecord ToggleColorSelection [color] +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (let [selected (get-in state [:dashboard :colors :selected])] +;; (rx/of +;; (if (selected color) +;; (DeselectColor. color) +;; (SelectColor. color)))))) +;; +;; (defn toggle-color-selection +;; [color] +;; {:pre [(color/hex? color)]} +;; (ToggleColorSelection. color)) +;; +;; ;; --- Copy Selected Color +;; +;; (defrecord CopySelected [id] +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [selected (get-in state [:dashboard :colors :selected])] +;; (update-in state [:colors-collections id :colors] set/union selected))) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (persist-collections)))) +;; +;; (defn copy-selected +;; [id] +;; {:pre [(or (uuid? id) (nil? id))]} +;; (CopySelected. id)) +;; +;; ;; --- Move Selected Color +;; +;; (defrecord MoveSelected [from to] +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [selected (get-in state [:dashboard :colors :selected])] +;; (-> state +;; (update-in [:colors-collections from :colors] set/difference selected) +;; (update-in [:colors-collections to :colors] set/union selected)))) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (persist-collections)))) +;; +;; (defn move-selected +;; [from to] +;; {:pre [(or (uuid? from) (nil? from)) +;; (or (uuid? to) (nil? to))]} +;; (MoveSelected. from to)) +;; +;; ;; --- Delete Colors +;; +;; (defrecord DeleteColors [coll-id colors] +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:dashboard :colors :selected] #{})) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (remove-colors coll-id colors)))) +;; +;; (defn delete-colors +;; [coll-id colors] +;; (DeleteColors. coll-id colors)) ;;;; NEW @@ -287,3 +287,43 @@ (update [_ state] (-> state (assoc-in [:library :selected-items] data))))) + +(declare create-color-library-result) + +(defn create-color-library + [team-id name] + (ptk/reify ::create-color-library + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/mutation! :create-color-library {:team-id team-id + :name name}) + (rx/map create-color-library-result))))) + +(defn create-color-library-result [result] + (ptk/reify ::create-color-library-result + ptk/UpdateEvent + (update [_ state] + (-> state + (update-in [:library :color-libraries] #(into [result] %)))))) + +(declare create-color-result) + +(defn create-color + [library-id color] + (s/assert (s/nilable uuid?) library-id) + (ptk/reify ::create-color + ptk/WatchEvent + (watch [_ state s] + + (->> (rp/mutation! :create-color {:library-id library-id + :content color + :name color}) + (rx/map create-color-result))))) + +(defn create-color-result + [item] + (ptk/reify ::create-color-result + ptk/UpdateEvent + (update [_ state] + (-> state + (update-in [:library :selected-items] #(into [item] %) ))))) diff --git a/frontend/src/uxbox/main/data/icons.cljs b/frontend/src/uxbox/main/data/icons.cljs index a7ac82f153..e5cc123fa5 100644 --- a/frontend/src/uxbox/main/data/icons.cljs +++ b/frontend/src/uxbox/main/data/icons.cljs @@ -76,6 +76,26 @@ (-> state (assoc-in [:library :selected-items] data))))) +(declare create-icon-library-result) + +(defn create-icon-library + [team-id name] + (ptk/reify ::create-icon-library + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/mutation! :create-icon-library {:team-id team-id + :name name}) + (rx/map create-icon-library-result))))) + +(defn create-icon-library-result [result] + (ptk/reify ::create-icon-library-result + ptk/UpdateEvent + (update [_ state] + (-> state + (update-in [:library :icon-libraries] #(into [result] %)))))) + + + ;; (declare fetch-icons) ;; ;; (defn initialize @@ -92,32 +112,32 @@ ;; ;; --- Fetch Collections -(declare collections-fetched) - -(def fetch-collections - (ptk/reify ::fetch-collections - ptk/WatchEvent - (watch [_ state s] - (->> (rp/query! :icons-collections) - (rx/map collections-fetched))))) - -;; --- Collections Fetched - -(defn collections-fetched - [items] - (s/assert (s/every ::collection) items) - (ptk/reify ::collections-fetched - cljs.core/IDeref - (-deref [_] items) - - ptk/UpdateEvent - (update [_ state] - (reduce (fn [state {:keys [id user] :as item}] - (let [type (if (uuid/zero? (:user-id item)) :builtin :own) - item (assoc item :type type)] - (assoc-in state [:icons-collections id] item))) - state - items)))) +;; (declare collections-fetched) +;; +;; (def fetch-collections +;; (ptk/reify ::fetch-collections +;; ptk/WatchEvent +;; (watch [_ state s] +;; (->> (rp/query! :icons-collections) +;; (rx/map collections-fetched))))) +;; +;; ;; --- Collections Fetched +;; +;; (defn collections-fetched +;; [items] +;; (s/assert (s/every ::collection) items) +;; (ptk/reify ::collections-fetched +;; cljs.core/IDeref +;; (-deref [_] items) +;; +;; ptk/UpdateEvent +;; (update [_ state] +;; (reduce (fn [state {:keys [id user] :as item}] +;; (let [type (if (uuid/zero? (:user-id item)) :builtin :own) +;; item (assoc item :type type)] +;; (assoc-in state [:icons-collections id] item))) +;; state +;; items)))) ;; ;; --- Create Collection @@ -175,77 +195,75 @@ ;; (rx/tap on-success) ;; (rx/ignore))))) ;; -;; ;; --- Icon Created -;; -;; (defrecord IconCreated [item] -;; ptk/UpdateEvent -;; (update [_ state] -;; (let [{:keys [id] :as item} (assoc item :type :icon)] -;; (update state :icons assoc id item)))) -;; -;; (defn icon-created -;; [item] -;; (IconCreated. item)) -;; -;; ;; --- Create Icon -;; -;; (declare icon-created) -;; -;; (defn- parse-svg -;; [data] -;; (s/assert ::us/string data) -;; (let [valid-tags #{"defs" "path" "circle" "rect" "metadata" "g" -;; "radialGradient" "stop"} -;; div (dom/create-element "div") -;; gc (dom/create-element "div") -;; g (dom/create-element "http://www.w3.org/2000/svg" "g") -;; _ (dom/set-html! div data) -;; svg (dom/query div "svg")] -;; (loop [child (dom/get-first-child svg)] -;; (if child -;; (let [tagname (dom/get-tag-name child)] -;; (if (contains? valid-tags tagname) -;; (dom/append-child! g child) -;; (dom/append-child! gc child)) -;; (recur (dom/get-first-child svg))) -;; (let [width (.. svg -width -baseVal -value) -;; height (.. svg -height -baseVal -value) -;; view-box [(.. svg -viewBox -baseVal -x) -;; (.. svg -viewBox -baseVal -y) -;; (.. svg -viewBox -baseVal -width) -;; (.. svg -viewBox -baseVal -height)] -;; props {:width width -;; :mimetype "image/svg+xml" -;; :height height -;; :view-box view-box}] -;; [(dom/get-outer-html g) props]))))) -;; -;; -;; (defn create-icons -;; [id files] -;; (s/assert (s/nilable uuid?) id) -;; (ptk/reify ::create-icons -;; ptk/WatchEvent -;; (watch [_ state s] -;; (letfn [(parse [file] -;; (->> (wapi/read-file-as-text file) -;; (rx/map parse-svg))) -;; (allowed? [file] -;; (= (.-type file) "image/svg+xml")) -;; (prepare [[content metadata]] -;; {:collection-id id -;; :content content -;; :id (uuid/next) -;; ;; TODO Keep the name of the original icon -;; :name (str "Icon " (gensym "i")) -;; :metadata metadata})] -;; (->> (rx/from files) -;; (rx/filter allowed?) -;; (rx/merge-map parse) -;; (rx/map prepare) -;; (rx/flat-map #(rp/mutation! :create-icon %)) -;; (rx/map icon-created)))))) -;; +;; --- Icon Created + +;; --- Create Icon +(defn- parse-svg + [data] + (s/assert ::us/string data) + (let [valid-tags #{"defs" "path" "circle" "rect" "metadata" "g" + "radialGradient" "stop"} + div (dom/create-element "div") + gc (dom/create-element "div") + g (dom/create-element "http://www.w3.org/2000/svg" "g") + _ (dom/set-html! div data) + svg (dom/query div "svg")] + (loop [child (dom/get-first-child svg)] + (if child + (let [tagname (dom/get-tag-name child)] + (if (contains? valid-tags tagname) + (dom/append-child! g child) + (dom/append-child! gc child)) + (recur (dom/get-first-child svg))) + (let [width (.. svg -width -baseVal -value) + height (.. svg -height -baseVal -value) + view-box [(.. svg -viewBox -baseVal -x) + (.. svg -viewBox -baseVal -y) + (.. svg -viewBox -baseVal -width) + (.. svg -viewBox -baseVal -height)] + props {:width width + :mimetype "image/svg+xml" + :height height + :view-box view-box}] + [(dom/get-outer-html g) props]))))) + + +(declare create-icon-result) + +(defn create-icons + [library-id files] + (s/assert (s/nilable uuid?) library-id) + (ptk/reify ::create-icons + ptk/WatchEvent + (watch [_ state s] + (letfn [(parse [file] + (->> (wapi/read-file-as-text file) + (rx/map parse-svg))) + (allowed? [file] + (= (.-type file) "image/svg+xml")) + (prepare [[content metadata]] + {:library-id library-id + :content content + :id (uuid/next) + ;; TODO Keep the name of the original icon + :name (str "Icon " (gensym "i")) + :metadata metadata})] + (->> (rx/from files) + (rx/filter allowed?) + (rx/merge-map parse) + (rx/map prepare) + (rx/flat-map #(rp/mutation! :create-icon %)) + (rx/map create-icon-result)))))) + +(defn create-icon-result + [item] + (ptk/reify ::create-icon-result + ptk/UpdateEvent + (update [_ state] + (let [{:keys [id] :as item} (assoc item :type :icon)] + (-> state + (update-in [:library :selected-items] #(into [item] %))))))) + ;; ;; --- Icon Persisted ;; ;; (defrecord IconPersisted [id data] @@ -272,27 +290,27 @@ ;; ;; --- Load Icons -(declare icons-fetched) - -(defn fetch-icons - [id] - (ptk/reify ::fetch-icons - ptk/WatchEvent - (watch [_ state s] - (let [params (cond-> {} id (assoc :collection-id id))] - (->> (rp/query! :icons-by-collection params) - (rx/map icons-fetched)))))) - -;; --- Icons Fetched - -(defn icons-fetched - [items] - ;; TODO: specs - (ptk/reify ::icons-fetched - ptk/UpdateEvent - (update [_ state] - (let [icons (d/index-by :id items)] - (assoc state :icons icons))))) +;; (declare icons-fetched) +;; +;; (defn fetch-icons +;; [id] +;; (ptk/reify ::fetch-icons +;; ptk/WatchEvent +;; (watch [_ state s] +;; (let [params (cond-> {} id (assoc :collection-id id))] +;; (->> (rp/query! :icons-by-collection params) +;; (rx/map icons-fetched)))))) +;; +;; ;; --- Icons Fetched +;; +;; (defn icons-fetched +;; [items] +;; ;; TODO: specs +;; (ptk/reify ::icons-fetched +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [icons (d/index-by :id items)] +;; (assoc state :icons icons))))) ;; ;; --- Rename Icon ;; diff --git a/frontend/src/uxbox/main/data/images.cljs b/frontend/src/uxbox/main/data/images.cljs index bbdbc881da..4d3cd53fe9 100644 --- a/frontend/src/uxbox/main/data/images.cljs +++ b/frontend/src/uxbox/main/data/images.cljs @@ -55,305 +55,254 @@ ::thumb-uri ::user-id])) -;; --- Initialize Collection Page - -(declare fetch-images) - -(defn initialize - [collection-id] - (us/verify ::us/uuid collection-id) - (ptk/reify ::initialize - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:dashboard-images :selected] #{})) - - ptk/WatchEvent - (watch [_ state stream] - (rx/of (fetch-images collection-id))))) - -;; --- Fetch Collections - -(declare collections-fetched) - -(def fetch-collections - (ptk/reify ::fetch-collections - ptk/WatchEvent - (watch [_ state s] - (->> (rp/query! :image-collections) - (rx/map collections-fetched))))) - - -;; --- Collections Fetched - -(defn collections-fetched - [items] - (us/verify (s/every ::collection) items) - (ptk/reify ::collections-fetched - ptk/UpdateEvent - (update [_ state] - (reduce (fn [state {:keys [id user] :as item}] - (let [type (if (uuid/zero? (:user-id item)) :builtin :own) - item (assoc item :type type)] - (assoc-in state [:images-collections id] item))) - state - items)))) - - -;; --- Create Collection - -(declare collection-created) - -(def create-collection - (ptk/reify ::create-collection - ptk/WatchEvent - (watch [_ state s] - (let [data {:name (tr "ds.default-library-title" (gensym "c"))}] - (->> (rp/mutation! :create-image-collection data) - (rx/map collection-created)))))) - -;; --- Collection Created - -(defn collection-created - [item] - (us/verify ::collection item) - (ptk/reify ::collection-created - ptk/UpdateEvent - (update [_ state] - (let [{:keys [id] :as item} (assoc item :type :own)] - (update state :images-collections assoc id item))))) - -;; --- Rename Collection - -(defn rename-collection - [id name] - (ptk/reify ::rename-collection - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:images-collections id :name] name)) - - ptk/WatchEvent - (watch [_ state s] - (let [params {:id id :name name}] - (->> (rp/mutation! :rename-image-collection params) - (rx/ignore)))))) - -;; --- Delete Collection - -(defn delete-collection - [id on-success] - (ptk/reify ::delete-collection - ptk/UpdateEvent - (update [_ state] - (update state :images-collections dissoc id)) - - ptk/WatchEvent - (watch [_ state s] - (->> (rp/mutation! :delete-image-collection {:id id}) - (rx/tap on-success) - (rx/ignore))))) - -;; --- Create Image - -(declare image-created) -(def allowed-file-types #{"image/jpeg" "image/png"}) - -(defn create-images - ([id files] (create-images id files identity)) - ([id files on-uploaded] - (us/verify (s/nilable ::us/uuid) id) - (us/verify fn? on-uploaded) - (ptk/reify ::create-images - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:dashboard-images :uploading] true)) - - ptk/WatchEvent - (watch [_ state stream] - (letfn [(allowed-file? [file] - (contains? allowed-file-types (.-type file))) - (finalize-upload [state] - (assoc-in state [:dashboard-images :uploading] false)) - (on-success [_] - (st/emit! finalize-upload) - (on-uploaded)) - (on-error [e] - (st/emit! finalize-upload) - (rx/throw e)) - (prepare [file] - {:name (.-name file) - :collection-id id - :content file})] - (->> (rx/from files) - (rx/filter allowed-file?) - (rx/map prepare) - (rx/mapcat #(rp/mutation! :upload-image %)) - (rx/reduce conj []) - (rx/do on-success) - (rx/mapcat identity) - (rx/map image-created) - (rx/catch on-error))))))) - -;; --- Image Created - -(defn image-created - [item] - (us/verify ::image item) - (ptk/reify ::image-created - ptk/UpdateEvent - (update [_ state] - (update state :images assoc (:id item) item)))) - -;; --- Update Image - -(defn persist-image - [id] - (us/verify ::us/uuid id) - (ptk/reify ::persist-image - ptk/WatchEvent - (watch [_ state stream] - (let [data (get-in state [:images id])] - (->> (rp/mutation! :update-image data) - (rx/ignore)))))) - -;; --- Fetch Images - -(declare images-fetched) - -(defn fetch-images - "Fetch a list of images of the selected collection" - [id] - (us/verify ::us/uuid id) - (ptk/reify ::fetch-images - ptk/WatchEvent - (watch [_ state s] - (let [params {:collection-id id}] - (->> (rp/query! :images-by-collection params) - (rx/map (partial images-fetched id))))))) - -;; --- Images Fetched - -(s/def ::images (s/every ::image)) - -(defn images-fetched - [collection-id items] - (us/verify ::us/uuid collection-id) - (us/verify ::images items) - (ptk/reify ::images-fetched - ptk/UpdateEvent - (update [_ state] - (let [images (d/index-by :id items)] - (assoc state :images images))))) - -;; --- Fetch Image - -(declare image-fetched) - -(defrecord FetchImage [id] - ptk/WatchEvent - (watch [_ state stream] - (let [existing (get-in state [:images id])] - (if existing - (rx/empty) - (->> (rp/query! :image-by-id {:id id}) - (rx/map image-fetched) - (rx/catch rp/client-error? #(rx/empty))))))) - -(defn fetch-image - "Conditionally fetch image by its id. If image - is already loaded, this event is noop." - [id] - {:pre [(uuid? id)]} - (FetchImage. id)) - -;; --- Image Fetched - -(defrecord ImageFetched [image] - ptk/UpdateEvent - (update [_ state] - (let [id (:id image)] - (update state :images assoc id image)))) - -(defn image-fetched - [image] - {:pre [(map? image)]} - (ImageFetched. image)) - -;; --- Rename Image - -(defn rename-image - [id name] - (us/verify ::us/uuid id) - (us/verify ::us/string name) - (ptk/reify ::rename-image - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:images id :name] name)) - - ptk/WatchEvent - (watch [_ state stream] - (rx/of (persist-image id))))) - -;; --- Image Selection - -(defn select-image - [id] - (ptk/reify ::select-image - ptk/UpdateEvent - (update [_ state] - (update-in state [:dashboard-images :selected] (fnil conj #{}) id)))) - -(defn deselect-image - [id] - (ptk/reify ::deselect-image - ptk/UpdateEvent - (update [_ state] - (update-in state [:dashboard-images :selected] (fnil disj #{}) id)))) - -(def deselect-all-images - (ptk/reify ::deselect-all-images - ptk/UpdateEvent - (update [_ state] - (assoc-in state [:dashboard-images :selected] #{})))) - -;; --- Delete Images - -(defn delete-image - [id] - (us/verify ::us/uuid id) - (ptk/reify ::delete-image - ptk/UpdateEvent - (update [_ state] - (update state :images dissoc id)) - - ptk/WatchEvent - (watch [_ state s] - (rx/merge - (rx/of deselect-all-images) - (->> (rp/mutation! :delete-image {:id id}) - (rx/ignore)))))) - -;; --- Delete Selected - -(def delete-selected - (ptk/reify ::delete-selected - ptk/WatchEvent - (watch [_ state stream] - (let [selected (get-in state [:dashboard-images :selected])] - (->> (rx/from selected) - (rx/map delete-image)))))) - -;; --- Update Opts (Filtering & Ordering) - -(defn update-opts - [& {:keys [order filter edition] - :or {edition false}}] - (ptk/reify ::update-opts - ptk/UpdateEvent - (update [_ state] - (update state :dashboard-images merge - {:edition edition} - (when order {:order order}) - (when filter {:filter filter}))))) +;; ;; --- Initialize Collection Page +;; +;; (declare fetch-images) +;; +;; (defn initialize +;; [collection-id] +;; (us/verify ::us/uuid collection-id) +;; (ptk/reify ::initialize +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:dashboard-images :selected] #{})) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (fetch-images collection-id))))) +;; +;; ;; --- Fetch Collections +;; +;; (declare collections-fetched) +;; +;; (def fetch-collections +;; (ptk/reify ::fetch-collections +;; ptk/WatchEvent +;; (watch [_ state s] +;; (->> (rp/query! :image-collections) +;; (rx/map collections-fetched))))) +;; +;; +;; ;; --- Collections Fetched +;; +;; (defn collections-fetched +;; [items] +;; (us/verify (s/every ::collection) items) +;; (ptk/reify ::collections-fetched +;; ptk/UpdateEvent +;; (update [_ state] +;; (reduce (fn [state {:keys [id user] :as item}] +;; (let [type (if (uuid/zero? (:user-id item)) :builtin :own) +;; item (assoc item :type type)] +;; (assoc-in state [:images-collections id] item))) +;; state +;; items)))) +;; +;; +;; ;; --- Create Collection +;; +;; (declare collection-created) +;; +;; (def create-collection +;; (ptk/reify ::create-collection +;; ptk/WatchEvent +;; (watch [_ state s] +;; (let [data {:name (tr "ds.default-library-title" (gensym "c"))}] +;; (->> (rp/mutation! :create-image-collection data) +;; (rx/map collection-created)))))) +;; +;; ;; --- Collection Created +;; +;; (defn collection-created +;; [item] +;; (us/verify ::collection item) +;; (ptk/reify ::collection-created +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [{:keys [id] :as item} (assoc item :type :own)] +;; (update state :images-collections assoc id item))))) +;; +;; ;; --- Rename Collection +;; +;; (defn rename-collection +;; [id name] +;; (ptk/reify ::rename-collection +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:images-collections id :name] name)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (let [params {:id id :name name}] +;; (->> (rp/mutation! :rename-image-collection params) +;; (rx/ignore)))))) +;; +;; ;; --- Delete Collection +;; +;; (defn delete-collection +;; [id on-success] +;; (ptk/reify ::delete-collection +;; ptk/UpdateEvent +;; (update [_ state] +;; (update state :images-collections dissoc id)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (->> (rp/mutation! :delete-image-collection {:id id}) +;; (rx/tap on-success) +;; (rx/ignore))))) +;; +;; ;; --- Update Image +;; +;; (defn persist-image +;; [id] +;; (us/verify ::us/uuid id) +;; (ptk/reify ::persist-image +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (let [data (get-in state [:images id])] +;; (->> (rp/mutation! :update-image data) +;; (rx/ignore)))))) +;; +;; ;; --- Fetch Images +;; +;; (declare images-fetched) +;; +;; (defn fetch-images +;; "Fetch a list of images of the selected collection" +;; [id] +;; (us/verify ::us/uuid id) +;; (ptk/reify ::fetch-images +;; ptk/WatchEvent +;; (watch [_ state s] +;; (let [params {:collection-id id}] +;; (->> (rp/query! :images-by-collection params) +;; (rx/map (partial images-fetched id))))))) +;; +;; ;; --- Images Fetched +;; +;; (s/def ::images (s/every ::image)) +;; +;; (defn images-fetched +;; [collection-id items] +;; (us/verify ::us/uuid collection-id) +;; (us/verify ::images items) +;; (ptk/reify ::images-fetched +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [images (d/index-by :id items)] +;; (assoc state :images images))))) +;; +;; ;; --- Fetch Image +;; +;; (declare image-fetched) +;; +;; (defrecord FetchImage [id] +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (let [existing (get-in state [:images id])] +;; (if existing +;; (rx/empty) +;; (->> (rp/query! :image-by-id {:id id}) +;; (rx/map image-fetched) +;; (rx/catch rp/client-error? #(rx/empty))))))) +;; +;; (defn fetch-image +;; "Conditionally fetch image by its id. If image +;; is already loaded, this event is noop." +;; [id] +;; {:pre [(uuid? id)]} +;; (FetchImage. id)) +;; +;; ;; --- Image Fetched +;; +;; (defrecord ImageFetched [image] +;; ptk/UpdateEvent +;; (update [_ state] +;; (let [id (:id image)] +;; (update state :images assoc id image)))) +;; +;; (defn image-fetched +;; [image] +;; {:pre [(map? image)]} +;; (ImageFetched. image)) +;; +;; ;; --- Rename Image +;; +;; (defn rename-image +;; [id name] +;; (us/verify ::us/uuid id) +;; (us/verify ::us/string name) +;; (ptk/reify ::rename-image +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:images id :name] name)) +;; +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (rx/of (persist-image id))))) +;; +;; ;; --- Image Selection +;; +;; (defn select-image +;; [id] +;; (ptk/reify ::select-image +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:dashboard-images :selected] (fnil conj #{}) id)))) +;; +;; (defn deselect-image +;; [id] +;; (ptk/reify ::deselect-image +;; ptk/UpdateEvent +;; (update [_ state] +;; (update-in state [:dashboard-images :selected] (fnil disj #{}) id)))) +;; +;; (def deselect-all-images +;; (ptk/reify ::deselect-all-images +;; ptk/UpdateEvent +;; (update [_ state] +;; (assoc-in state [:dashboard-images :selected] #{})))) +;; +;; ;; --- Delete Images +;; +;; (defn delete-image +;; [id] +;; (us/verify ::us/uuid id) +;; (ptk/reify ::delete-image +;; ptk/UpdateEvent +;; (update [_ state] +;; (update state :images dissoc id)) +;; +;; ptk/WatchEvent +;; (watch [_ state s] +;; (rx/merge +;; (rx/of deselect-all-images) +;; (->> (rp/mutation! :delete-image {:id id}) +;; (rx/ignore)))))) +;; +;; ;; --- Delete Selected +;; +;; (def delete-selected +;; (ptk/reify ::delete-selected +;; ptk/WatchEvent +;; (watch [_ state stream] +;; (let [selected (get-in state [:dashboard-images :selected])] +;; (->> (rx/from selected) +;; (rx/map delete-image)))))) +;; +;; ;; --- Update Opts (Filtering & Ordering) +;; +;; (defn update-opts +;; [& {:keys [order filter edition] +;; :or {edition false}}] +;; (ptk/reify ::update-opts +;; ptk/UpdateEvent +;; (update [_ state] +;; (update state :dashboard-images merge +;; {:edition edition} +;; (when order {:order order}) +;; (when filter {:filter filter}))))) ;; --- Copy Selected Image @@ -431,7 +380,7 @@ ptk/WatchEvent (watch [_ state stream] - (->> (rp/query! :image {:library-id library-id}) + (->> (rp/query! :images {:library-id library-id}) (rx/map fetch-image-library-result))))) (defn fetch-image-library-result @@ -441,3 +390,71 @@ (update [_ state] (-> state (assoc-in [:library :selected-items] data))))) + +(declare create-image-library-result) + +(defn create-image-library + [team-id name] + (ptk/reify ::create-image-library + ptk/WatchEvent + (watch [_ state stream] + (->> (rp/mutation! :create-image-library {:team-id team-id + :name name}) + (rx/map create-image-library-result))))) + +(defn create-image-library-result [result] + (ptk/reify ::create-image-library-result + ptk/UpdateEvent + (update [_ state] + (-> state + (update-in [:library :image-libraries] #(into [result] %)))))) + + + +;; --- Create Image +(declare create-images-result) +(def allowed-file-types #{"image/jpeg" "image/png"}) + +(defn create-images + ([library-id files] (create-images library-id files identity)) + ([library-id files on-uploaded] + (us/verify (s/nilable ::us/uuid) library-id) + (us/verify fn? on-uploaded) + (ptk/reify ::create-images + ptk/WatchEvent + (watch [_ state stream] + (letfn [(allowed-file? [file] + (contains? allowed-file-types (.-type file))) + #_(finalize-upload [state] + (assoc-in state [:dashboard-images :uploading] false)) + (on-success [_] + #_(st/emit! finalize-upload) + (on-uploaded)) + (on-error [e] + #_(st/emit! finalize-upload) + (rx/throw e)) + (prepare [file] + {:name (.-name file) + :library-id library-id + :content file})] + (->> (rx/from files) + (rx/filter allowed-file?) + (rx/map prepare) + (rx/mapcat #(rp/mutation! :upload-image %)) + (rx/reduce conj []) + (rx/do on-success) + (rx/mapcat identity) + (rx/map create-images-result) + (rx/catch on-error))))))) + +;; --- Image Created + +(defn create-images-result + [item] + #_(us/verify ::image item) + (ptk/reify ::create-images-result + ptk/UpdateEvent + (update [_ state] + (-> state + (update-in [:library :selected-items] #(into [item] %)))))) + diff --git a/frontend/src/uxbox/main/ui/colorpicker.cljs b/frontend/src/uxbox/main/ui/colorpicker.cljs index 57c69a60d6..e22023cd6c 100644 --- a/frontend/src/uxbox/main/ui/colorpicker.cljs +++ b/frontend/src/uxbox/main/ui/colorpicker.cljs @@ -6,6 +6,8 @@ (ns uxbox.main.ui.colorpicker (:require + [lentes.core :as l] + [uxbox.main.store :as st] [goog.object :as gobj] [rumext.alpha :as mf] [vendor.react-color])) @@ -16,5 +18,21 @@ [:> js/SketchPicker {:color value :disableAlpha true :presetColors colors - :onChangeComplete on-change-complete}])) + :onChangeComplete on-change-complete + :style {:box-shadow "none"}}])) +(defn- lookup-colors + [state] + (as-> {} $ + (reduce (fn [acc shape] + (-> acc + (update (:fill-color shape) (fnil inc 0)) + (update (:stroke-color shape) (fnil inc 0)))) + $ (vals (:shapes state))) + (reverse (sort-by second $)) + (map first $) + (remove nil? $))) + +(def most-used-colors + (-> (l/lens lookup-colors) + (l/derive st/state))) diff --git a/frontend/src/uxbox/main/ui/dashboard/colors.cljs b/frontend/src/uxbox/main/ui/dashboard/colors.cljs index dbea27b193..c9d17afa79 100644 --- a/frontend/src/uxbox/main/ui/dashboard/colors.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/colors.cljs @@ -24,272 +24,272 @@ [uxbox.util.i18n :as t :refer [tr]] [uxbox.util.router :as rt])) -;; --- Refs - -(def collections-iref - (-> (l/key :colors-collections) - (l/derive st/state))) - -(def selected-colors-iref - (-> (l/in [:dashboard :colors :selected]) - (l/derive st/state))) - -;; --- Colors Modal (Component) - -(mf/defc color-modal - [{:keys [on-submit value] :as props}] - (let [local (mf/use-var value)] - [:div.lightbox-body - [:h3 (tr "ds.color-lightbox.title" )] - [:form - [:div.row-flex.center - [:& colorpicker {:value (or @local "#00ccff") - :on-change #(reset! local %)}]] - [:input#project-btn.btn-primary - {:value (tr "ds.color-lightbox.add") - :on-click #(on-submit @local) - :type "button"}]]])) - -;; --- Page Title - - -(mf/defc grid-header - [{:keys [coll] :as props}] - (letfn [(on-change [name] - (st/emit! (dc/rename-collection (:id coll) name))) - - (delete [] - (st/emit! - (dc/delete-collection (:id coll)) - (rt/nav :dashboard-colors nil {:type (:type coll)}))) - - (on-delete [] - (modal/show! confirm-dialog {:on-accept delete}))] - [:& common/grid-header {:value (:name coll) - :on-change on-change - :on-delete on-delete}])) - -;; --- Nav - -(mf/defc nav-item - [{:keys [coll selected?] :as props}] - (let [local (mf/use-state {}) - {:keys [id type name]} coll - colors (count (:colors coll)) - editable? (= type :own)] - (letfn [(on-click [event] - (let [type (or type :own)] - (st/emit! (rt/nav :dashboard-colors nil {:type type :id id})))) - (on-input-change [event] - (let [value (dom/get-target event) - value (dom/get-value value)] - (swap! local assoc :name value))) - (on-cancel [event] - (swap! local dissoc :name) - (swap! local dissoc :edit)) - (on-double-click [event] - (when editable? - (swap! local assoc :edit true))) - (on-input-keyup [event] - (when (k/enter? event) - (let [value (dom/get-target event) - value (dom/get-value value)] - (st/emit! (dc/rename-collection id (str/trim (:name @local)))) - (swap! local assoc :edit false))))] - [:li {:on-click on-click - :on-double-click on-double-click - :class-name (when selected? "current")} - (if (:edit @local) - [:div - [:input.element-title - {:value (if (:name @local) (:name @local) name) - :on-change on-input-change - :on-key-down on-input-keyup}] - [:span.close {:on-click on-cancel} i/close]] - [:span.element-title name]) - #_[:span.element-subtitle - (tr "ds.num-elements" (t/c colors))]]))) - -(mf/defc nav - [{:keys [id type colls selected-coll] :as props}] - (let [own? (= type :own) - builtin? (= type :builtin) - select-tab #(st/emit! (rt/nav :dashboard-colors nil {:type %}))] - [:div.library-bar - [:div.library-bar-inside - [:ul.library-tabs - [:li {:class-name (when own? "current") - :on-click (partial select-tab :own)} - (tr "ds.your-colors-title")] - [:li {:class-name (when builtin? "current") - :on-click (partial select-tab :builtin)} - (tr "ds.store-colors-title")]] - [:ul.library-elements - (when own? - [:li - [:a.btn-primary {:on-click #(st/emit! (dc/create-collection))} - (tr "ds.colors-collection.new")]]) - (for [item colls] - (let [selected? (= (:id item) (:id selected-coll))] - [:& nav-item {:coll item :selected? selected? :key (:id item)}]))]]])) - -;; --- Grid - -(mf/defc grid-form - [{:keys [id] :as props}] - (letfn [(on-submit [val] - (st/emit! (dc/add-color id val)) - (modal/hide!)) - (on-click [event] - (modal/show! color-modal {:on-submit on-submit}))] - [:div.grid-item.small-item.add-project {:on-click on-click} - [:span (tr "ds.color-new")]])) - -(mf/defc grid-options-tooltip - [{:keys [selected on-select title] :as props}] - {:pre [(uuid? selected) - (fn? on-select) - (string? title)]} - (let [colls (mf/deref collections-iref) - colls (->> (vals colls) - (filter #(= :own (:type %))) - (remove #(= selected (:id %))) - (sort-by :name colls)) - on-select (fn [event id] - (dom/prevent-default event) - (dom/stop-propagation event) - (on-select id))] - [:ul.move-list - [:li.title title] - (for [{:keys [id name] :as coll} colls] - [:li {:key (str id)} - [:a {:on-click #(on-select % id)} name]])])) - -(mf/defc grid-options - [{:keys [id type coll selected] :as props}] - (let [local (mf/use-state {})] - (letfn [(delete [event] - (st/emit! (dc/delete-colors id selected))) - (on-delete [event] - (modal/show! confirm-dialog {:on-accept delete})) - (on-toggle-copy [event] - (swap! local update :show-copy-tooltip not) - (swap! local assoc :show-move-tooltip false)) - (on-toggle-move [event] - (swap! local update :show-move-tooltip not) - (swap! local assoc :show-copy-tooltip false)) - (on-copy [selected] - (swap! local assoc - :show-move-tooltip false - :show-copy-tooltip false) - (st/emit! (dc/copy-selected selected))) - (on-move [selected] - (swap! local assoc - :show-move-tooltip false - :show-copy-tooltip false) - (st/emit! (dc/move-selected id selected)))] - - ;; MULTISELECT OPTIONS BAR - [:div.multiselect-bar - (if (or (= type :own) (nil? id)) - ;; if editable - [:div.multiselect-nav - [:span.move-item.tooltip.tooltip-top - {:alt (tr "ds.multiselect-bar.copy") - :on-click on-toggle-copy} - (when (:show-copy-tooltip @local) - [:& grid-options-tooltip {:selected id - :title (tr "ds.multiselect-bar.copy-to-library") - :on-select on-copy}]) - i/copy] - [:span.move-item.tooltip.tooltip-top - {:alt (tr "ds.multiselect-bar.move") - :on-click on-toggle-move} - (when (:show-move-tooltip @local) - [:& grid-options-tooltip {:selected id - :title (tr "ds.multiselect-bar.move-to-library") - :on-select on-move}]) - i/move] - [:span.delete.tooltip.tooltip-top - {:alt (tr "ds.multiselect-bar.delete") - :on-click on-delete} - i/trash]] - - ;; if not editable - [:div.multiselect-nav - [:span.move-item.tooltip.tooltip-top - {:alt (tr "ds.multiselect-bar.copy") - :on-click on-toggle-copy} - (when (:show-copy-tooltip @local) - [:& grid-options-tooltip {:selected id - :title (tr "ds.multiselect-bar.copy-to-library") - :on-select on-copy}]) - i/organize]])]))) - -(mf/defc grid-item - [{:keys [color selected?] :as props}] - (letfn [(toggle-selection [event] - (st/emit! (dc/toggle-color-selection color)))] - [:div.grid-item.small-item.project-th {:on-click toggle-selection} - [:span.color-swatch {:style {:background-color color}}] - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id color - :on-change toggle-selection - :checked selected?}] - [:label {:for color}]] - [:span.color-data color] - [:span.color-data (apply str "RGB " (interpose ", " (hex->rgb color)))]])) - -(mf/defc grid - [{:keys [id type coll selected] :as props}] - (let [{:keys [colors]} coll - editable? (= :own type) - colors (->> (remove nil? colors) - (sort-by identity))] - [:div.dashboard-grid-content - [:div.dashboard-grid-row - (when (and editable? id) - [:& grid-form {:id id}]) - (for [color colors] - (let [selected? (contains? selected color)] - [:& grid-item {:color color :selected? selected? :key color}]))]])) - -(mf/defc content - [{:keys [id type coll] :as props}] - (let [selected (mf/deref selected-colors-iref)] - [:section.dashboard-grid.library - (when coll - [:& grid-header {:coll coll}]) - [:& grid {:coll coll :id id :type type :selected selected}] - (when (seq selected) - [:& grid-options {:id id :type type - :selected selected - :coll coll}])])) - -;; --- Colors Page - -(mf/defc colors-page - [{:keys [id type] :as props}] - (let [type (or type :own) - - colls (mf/deref collections-iref) - colls (cond->> (vals colls) - (= type :own) (filter #(= :own (:type %))) - (= type :builtin) (filter #(= :builtin (:type %))) - true (sort-by :created-at)) - selected-coll (if id - (seek #(= id (:id %)) colls) - (first colls)) - id (:id selected-coll)] - - (mf/use-effect #(st/emit! (dc/fetch-collections))) - - [:section.dashboard-content - [:& nav {:type type - :id id - :colls colls}] - [:& content {:type type - :id id - :coll selected-coll}]])) - +;; ;; --- Refs +;; +;; (def collections-iref +;; (-> (l/key :colors-collections) +;; (l/derive st/state))) +;; +;; (def selected-colors-iref +;; (-> (l/in [:dashboard :colors :selected]) +;; (l/derive st/state))) +;; +;; ;; --- Colors Modal (Component) +;; +;; (mf/defc color-modal +;; [{:keys [on-submit value] :as props}] +;; (let [local (mf/use-var value)] +;; [:div.lightbox-body +;; [:h3 (tr "ds.color-lightbox.title" )] +;; [:form +;; [:div.row-flex.center +;; [:& colorpicker {:value (or @local "#00ccff") +;; :on-change #(reset! local %)}]] +;; [:input#project-btn.btn-primary +;; {:value (tr "ds.color-lightbox.add") +;; :on-click #(on-submit @local) +;; :type "button"}]]])) +;; +;; ;; --- Page Title +;; +;; +;; (mf/defc grid-header +;; [{:keys [coll] :as props}] +;; (letfn [(on-change [name] +;; (st/emit! (dc/rename-collection (:id coll) name))) +;; +;; (delete [] +;; (st/emit! +;; (dc/delete-collection (:id coll)) +;; (rt/nav :dashboard-colors nil {:type (:type coll)}))) +;; +;; (on-delete [] +;; (modal/show! confirm-dialog {:on-accept delete}))] +;; [:& common/grid-header {:value (:name coll) +;; :on-change on-change +;; :on-delete on-delete}])) +;; +;; ;; --- Nav +;; +;; (mf/defc nav-item +;; [{:keys [coll selected?] :as props}] +;; (let [local (mf/use-state {}) +;; {:keys [id type name]} coll +;; colors (count (:colors coll)) +;; editable? (= type :own)] +;; (letfn [(on-click [event] +;; (let [type (or type :own)] +;; (st/emit! (rt/nav :dashboard-colors nil {:type type :id id})))) +;; (on-input-change [event] +;; (let [value (dom/get-target event) +;; value (dom/get-value value)] +;; (swap! local assoc :name value))) +;; (on-cancel [event] +;; (swap! local dissoc :name) +;; (swap! local dissoc :edit)) +;; (on-double-click [event] +;; (when editable? +;; (swap! local assoc :edit true))) +;; (on-input-keyup [event] +;; (when (k/enter? event) +;; (let [value (dom/get-target event) +;; value (dom/get-value value)] +;; (st/emit! (dc/rename-collection id (str/trim (:name @local)))) +;; (swap! local assoc :edit false))))] +;; [:li {:on-click on-click +;; :on-double-click on-double-click +;; :class-name (when selected? "current")} +;; (if (:edit @local) +;; [:div +;; [:input.element-title +;; {:value (if (:name @local) (:name @local) name) +;; :on-change on-input-change +;; :on-key-down on-input-keyup}] +;; [:span.close {:on-click on-cancel} i/close]] +;; [:span.element-title name]) +;; #_[:span.element-subtitle +;; (tr "ds.num-elements" (t/c colors))]]))) +;; +;; (mf/defc nav +;; [{:keys [id type colls selected-coll] :as props}] +;; (let [own? (= type :own) +;; builtin? (= type :builtin) +;; select-tab #(st/emit! (rt/nav :dashboard-colors nil {:type %}))] +;; [:div.library-bar +;; [:div.library-bar-inside +;; [:ul.library-tabs +;; [:li {:class-name (when own? "current") +;; :on-click (partial select-tab :own)} +;; (tr "ds.your-colors-title")] +;; [:li {:class-name (when builtin? "current") +;; :on-click (partial select-tab :builtin)} +;; (tr "ds.store-colors-title")]] +;; [:ul.library-elements +;; (when own? +;; [:li +;; [:a.btn-primary {:on-click #(st/emit! (dc/create-collection))} +;; (tr "ds.colors-collection.new")]]) +;; (for [item colls] +;; (let [selected? (= (:id item) (:id selected-coll))] +;; [:& nav-item {:coll item :selected? selected? :key (:id item)}]))]]])) +;; +;; ;; --- Grid +;; +;; (mf/defc grid-form +;; [{:keys [id] :as props}] +;; (letfn [(on-submit [val] +;; (st/emit! (dc/add-color id val)) +;; (modal/hide!)) +;; (on-click [event] +;; (modal/show! color-modal {:on-submit on-submit}))] +;; [:div.grid-item.small-item.add-project {:on-click on-click} +;; [:span (tr "ds.color-new")]])) +;; +;; (mf/defc grid-options-tooltip +;; [{:keys [selected on-select title] :as props}] +;; {:pre [(uuid? selected) +;; (fn? on-select) +;; (string? title)]} +;; (let [colls (mf/deref collections-iref) +;; colls (->> (vals colls) +;; (filter #(= :own (:type %))) +;; (remove #(= selected (:id %))) +;; (sort-by :name colls)) +;; on-select (fn [event id] +;; (dom/prevent-default event) +;; (dom/stop-propagation event) +;; (on-select id))] +;; [:ul.move-list +;; [:li.title title] +;; (for [{:keys [id name] :as coll} colls] +;; [:li {:key (str id)} +;; [:a {:on-click #(on-select % id)} name]])])) +;; +;; (mf/defc grid-options +;; [{:keys [id type coll selected] :as props}] +;; (let [local (mf/use-state {})] +;; (letfn [(delete [event] +;; (st/emit! (dc/delete-colors id selected))) +;; (on-delete [event] +;; (modal/show! confirm-dialog {:on-accept delete})) +;; (on-toggle-copy [event] +;; (swap! local update :show-copy-tooltip not) +;; (swap! local assoc :show-move-tooltip false)) +;; (on-toggle-move [event] +;; (swap! local update :show-move-tooltip not) +;; (swap! local assoc :show-copy-tooltip false)) +;; (on-copy [selected] +;; (swap! local assoc +;; :show-move-tooltip false +;; :show-copy-tooltip false) +;; (st/emit! (dc/copy-selected selected))) +;; (on-move [selected] +;; (swap! local assoc +;; :show-move-tooltip false +;; :show-copy-tooltip false) +;; (st/emit! (dc/move-selected id selected)))] +;; +;; ;; MULTISELECT OPTIONS BAR +;; [:div.multiselect-bar +;; (if (or (= type :own) (nil? id)) +;; ;; if editable +;; [:div.multiselect-nav +;; [:span.move-item.tooltip.tooltip-top +;; {:alt (tr "ds.multiselect-bar.copy") +;; :on-click on-toggle-copy} +;; (when (:show-copy-tooltip @local) +;; [:& grid-options-tooltip {:selected id +;; :title (tr "ds.multiselect-bar.copy-to-library") +;; :on-select on-copy}]) +;; i/copy] +;; [:span.move-item.tooltip.tooltip-top +;; {:alt (tr "ds.multiselect-bar.move") +;; :on-click on-toggle-move} +;; (when (:show-move-tooltip @local) +;; [:& grid-options-tooltip {:selected id +;; :title (tr "ds.multiselect-bar.move-to-library") +;; :on-select on-move}]) +;; i/move] +;; [:span.delete.tooltip.tooltip-top +;; {:alt (tr "ds.multiselect-bar.delete") +;; :on-click on-delete} +;; i/trash]] +;; +;; ;; if not editable +;; [:div.multiselect-nav +;; [:span.move-item.tooltip.tooltip-top +;; {:alt (tr "ds.multiselect-bar.copy") +;; :on-click on-toggle-copy} +;; (when (:show-copy-tooltip @local) +;; [:& grid-options-tooltip {:selected id +;; :title (tr "ds.multiselect-bar.copy-to-library") +;; :on-select on-copy}]) +;; i/organize]])]))) +;; +;; (mf/defc grid-item +;; [{:keys [color selected?] :as props}] +;; (letfn [(toggle-selection [event] +;; (st/emit! (dc/toggle-color-selection color)))] +;; [:div.grid-item.small-item.project-th {:on-click toggle-selection} +;; [:span.color-swatch {:style {:background-color color}}] +;; [:div.input-checkbox.check-primary +;; [:input {:type "checkbox" +;; :id color +;; :on-change toggle-selection +;; :checked selected?}] +;; [:label {:for color}]] +;; [:span.color-data color] +;; [:span.color-data (apply str "RGB " (interpose ", " (hex->rgb color)))]])) +;; +;; (mf/defc grid +;; [{:keys [id type coll selected] :as props}] +;; (let [{:keys [colors]} coll +;; editable? (= :own type) +;; colors (->> (remove nil? colors) +;; (sort-by identity))] +;; [:div.dashboard-grid-content +;; [:div.dashboard-grid-row +;; (when (and editable? id) +;; [:& grid-form {:id id}]) +;; (for [color colors] +;; (let [selected? (contains? selected color)] +;; [:& grid-item {:color color :selected? selected? :key color}]))]])) +;; +;; (mf/defc content +;; [{:keys [id type coll] :as props}] +;; (let [selected (mf/deref selected-colors-iref)] +;; [:section.dashboard-grid.library +;; (when coll +;; [:& grid-header {:coll coll}]) +;; [:& grid {:coll coll :id id :type type :selected selected}] +;; (when (seq selected) +;; [:& grid-options {:id id :type type +;; :selected selected +;; :coll coll}])])) +;; +;; ;; --- Colors Page +;; +;; (mf/defc colors-page +;; [{:keys [id type] :as props}] +;; (let [type (or type :own) +;; +;; colls (mf/deref collections-iref) +;; colls (cond->> (vals colls) +;; (= type :own) (filter #(= :own (:type %))) +;; (= type :builtin) (filter #(= :builtin (:type %))) +;; true (sort-by :created-at)) +;; selected-coll (if id +;; (seek #(= id (:id %)) colls) +;; (first colls)) +;; id (:id selected-coll)] +;; +;; (mf/use-effect #(st/emit! (dc/fetch-collections))) +;; +;; [:section.dashboard-content +;; [:& nav {:type type +;; :id id +;; :colls colls}] +;; [:& content {:type type +;; :id id +;; :coll selected-coll}]])) +;; diff --git a/frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs b/frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs deleted file mode 100644 index dd55fb276d..0000000000 --- a/frontend/src/uxbox/main/ui/dashboard/components/context_menu.cljs +++ /dev/null @@ -1,15 +0,0 @@ -(ns uxbox.main.ui.dashboard.components.context-menu - (:require - [rumext.alpha :as mf] - [uxbox.util.uuid :as uuid])) - -(mf/defc context-menu - [{ :keys [ is-open options ]}] - [:div.context-menu - { :class-name (when is-open "is-open")} - [:ul.context-menu-items - (for [[action-name action-handler] options] - [:li.context-menu-item - { :key (uuid/next)} - [:a.context-menu-action {:on-click action-handler} action-name]])]]) - diff --git a/frontend/src/uxbox/main/ui/dashboard/images.cljs b/frontend/src/uxbox/main/ui/dashboard/images.cljs index 3825ff26c3..e495c51e84 100644 --- a/frontend/src/uxbox/main/ui/dashboard/images.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/images.cljs @@ -28,360 +28,360 @@ [uxbox.util.router :as rt] [uxbox.util.time :as dt])) -;; --- Page Title - -(mf/defc grid-header - [{:keys [collection] :as props}] - (let [{:keys [id type]} collection - on-change #(st/emit! (di/rename-collection id %)) - on-deleted #(st/emit! (rt/nav :dashboard-images nil {:type type})) - delete #(st/emit! (di/delete-collection id on-deleted)) - on-delete #(modal/show! confirm-dialog {:on-accept delete})] - [:& common/grid-header {:value (:name collection) - :on-change on-change - :on-delete on-delete}])) - -;; --- Nav - -(mf/defc nav-item - [{:keys [coll selected?] :as props}] - (let [local (mf/use-state {}) - {:keys [id type name num-images]} coll - editable? (= type :own) - - on-click - (fn [event] - (let [type (or type :own)] - (st/emit! (rt/nav :dashboard-images {} {:type type :id id})))) - - on-cancel-edition #(swap! local dissoc :edit) - on-double-click #(when editable? (swap! local assoc :edit true)) - - on-input-keyup - (fn [event] - (when (kbd/enter? event) - (let [value (-> (dom/get-target event) - (dom/get-value) - (str/trim))] - (st/emit! (di/rename-collection id value)) - (swap! local assoc :edit false))))] - - [:li {:on-click on-click - :on-double-click on-double-click - :class-name (when selected? "current")} - (if (:edit @local) - [:div - [:input.element-title {:default-value name - :on-key-down on-input-keyup}] - [:span.close {:on-click on-cancel-edition} i/close]] - [:span.element-title (if id name "Storage")])])) - -(mf/defc nav - [{:keys [id type collections] :as props}] - (let [locale (i18n/use-locale) - own? (= type :own) - builtin? (= type :builtin) - create-collection #(st/emit! di/create-collection) - select-own-tab #(st/emit! (rt/nav :dashboard-images nil {:type :own})) - select-buitin-tab #(st/emit! (rt/nav :dashboard-images nil {:type :builtin}))] - [:div.library-bar - [:div.library-bar-inside - - ;; Tabs - [:ul.library-tabs - [:li {:class (when own? "current") - :on-click select-own-tab} - (t locale "ds.your-images-title")] - - [:li {:class (when builtin? "current") - :on-click select-buitin-tab} - (t locale "ds.store-images-title")]] - - ;; Collections List - [:ul.library-elements - (when own? - [:li - [:a.btn-primary {:on-click create-collection} - (t locale "ds.images-collection.new")]]) - - (for [item collections] - [:& nav-item {:coll item - :selected? (= (:id item) id) - :key (:id item)}])]]])) - -;; --- Grid - -;; (mf/defc grid-options-tooltip -;; [{:keys [selected on-select title] :as props}] -;; {:pre [(uuid? selected) -;; (fn? on-select) -;; (string? title)]} -;; (let [colls (mf/deref collections-iref) -;; colls (->> (vals colls) -;; (filter #(= :own (:type %))) -;; (remove #(= selected (:id %))) -;; #_(sort-by :name colls)) -;; on-select (fn [event id] -;; (dom/prevent-default event) -;; (dom/stop-propagation event) -;; (on-select id))] -;; [:ul.move-list -;; [:li.title title] -;; [:li -;; (when (not (nil? selected)) -;; [:a {:href "#" :on-click #(on-select % nil)} "Storage"])] -;; (for [{:keys [id name] :as coll} colls] -;; [:li {:key (pr-str id)} -;; [:a {:on-click #(on-select % id)} name]])])) - -(mf/defc grid-options - [{:keys [id type selected] :as props}] - (let [local (mf/use-state {}) - delete #(st/emit! di/delete-selected) - on-delete #(modal/show! confirm-dialog {:on-accept delete}) - - ;; (on-toggle-copy [event] - ;; (swap! local update :show-copy-tooltip not)) - ;; (on-toggle-move [event] - ;; (swap! local update :show-move-tooltip not)) - ;; (on-copy [selected] - ;; (swap! local assoc - ;; :show-move-tooltip false - ;; :show-copy-tooltip false) - ;; (st/emit! (di/copy-selected selected))) - ;; (on-move [selected] - ;; (swap! local assoc - ;; :show-move-tooltip false - ;; :show-copy-tooltip false) - ;; (st/emit! (di/move-selected selected))) - ;; (on-rename [event] - ;; (let [selected (first selected)] - ;; (st/emit! (di/update-opts :edition selected)))) - ] - ;; MULTISELECT OPTIONS BAR - [:div.multiselect-bar - (when (= type :own) - ;; If editable - [:div.multiselect-nav - ;; [:span.move-item.tooltip.tooltip-top - ;; {:alt (tr "ds.multiselect-bar.copy") - ;; :on-click on-toggle-copy} - ;; (when (:show-copy-tooltip @local) - ;; [:& grid-options-tooltip {:selected id - ;; :title (tr "ds.multiselect-bar.copy-to-library") - ;; :on-select on-copy}]) - ;; i/copy] - ;; [:span.move-item.tooltip.tooltip-top - ;; {:alt (tr "ds.multiselect-bar.move") - ;; :on-click on-toggle-move} - ;; (when (:show-move-tooltip @local) - ;; [:& grid-options-tooltip {:selected id - ;; :title (tr "ds.multiselect-bar.move-to-library") - ;; :on-select on-move}]) - ;; i/move] - ;; (when (= 1 (count selected)) - ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.rename") - ;; :on-click on-rename} - ;; i/pencil]) - [:span.delete.tooltip.tooltip-top - {:alt (tr "ds.multiselect-bar.delete") - :on-click on-delete} - i/trash]] - - ;; If not editable - ;; [:div.multiselect-nav - ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.copy") - ;; :on-click on-toggle-copy} - ;; (when (:show-copy-tooltip @local) - ;; [:& grid-options-tooltip {:selected id - ;; :title (tr "ds.multiselect-bar.copy-to-library") - ;; :on-select on-copy}]) - ;; i/organize]] - )])) - - -;; --- Grid Form - -(mf/defc grid-form - [{:keys [id type uploading?] :as props}] - (let [input (mf/use-ref nil) - on-click #(dom/click (mf/ref-node input)) - on-select #(st/emit! (->> (dom/get-target %) - (dom/get-files) - (array-seq) - (di/create-images id)))] - [:div.grid-item.add-project {:on-click on-click} - (if uploading? - [:div i/loader-pencil] - [:span (tr "ds.image-new")]) - [:input.upload-image-input - {:style {:display "none"} - :multiple true - :ref input - :value "" - :accept "image/jpeg,image/png,image/webp" - :type "file" - :on-change on-select}]])) - -;; --- Grid Item - -(mf/defc grid-item - [{:keys [image selected? edition?] :as props}] - (let [toggle-selection #(st/emit! (if selected? - (di/deselect-image (:id image)) - (di/select-image (:id image)))) - on-blur - (fn [event] - (let [target (dom/get-target event) - name (dom/get-value target)] - (st/emit! (di/update-opts :edition false) - (di/rename-image (:id image) name)))) - - on-key-down - (fn [event] - (when (kbd/enter? event) - (on-blur event))) - - on-edit - (fn [event] - (dom/stop-propagation event) - (dom/prevent-default event) - (st/emit! (di/update-opts :edition (:id image)))) - - background (str "url('" (:thumb-uri image) "')")] - - [:div.grid-item.images-th - [:div.grid-item-th {:style {:background-image background}} - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id (:id image) - :on-change toggle-selection - :checked selected?}] - [:label {:for (:id image)}]]] - - [:div.item-info - (if edition? - [:input.element-name {:type "text" - :auto-focus true - :on-key-down on-key-down - :on-blur on-blur - :on-click on-edit - :default-value (:name image)}] - [:h3 {:on-double-click on-edit} (:name image)]) - [:span.date (tr "ds.uploaded-at" (dt/format (:created-at image) "dd/MM/yyyy"))]]])) - -;; --- Grid - -;; (defn- make-images-iref -;; [collection-id] -;; (letfn [(selector [state] -;; (->> (vals (:images state)) -;; (filterv #(= (:collection-id %) collection-id))))] -;; (-> (l/lens selector) -;; (l/derive st/state)))) - -(def images-iref - (-> (comp (l/key :images) (l/lens vals)) - (l/derive st/state))) - -(mf/defc grid - [{:keys [id type collection opts] :as props}] - (let [editable? (= type :own) - ;; images-iref (mf/use-memo {:fn #(make-images-iref id) - ;; :deps (mf/deps id)}) - images (->> (mf/deref images-iref) - (sort-by :created-at))] - [:div.dashboard-grid-content - [:div.dashboard-grid-row - (when editable? - [:& grid-form {:id id :type type :uploading? (:uploading opts)}]) - (for [item images] - [:& grid-item {:image item - :key (:id item) - :selected? (contains? (:selected opts) (:id item)) - :edition? (= (:edition opts) (:id item))}])]])) - -;; --- Menu - -;; (mf/defc menu -;; [{:keys [opts coll] :as props}] -;; (let [ordering (:order opts :name) -;; filtering (:filter opts "") -;; icount (count (:images coll))] -;; (letfn [(on-term-change [event] -;; (let [term (-> (dom/get-target event) -;; (dom/get-value))] -;; (st/emit! (di/update-opts :filter term)))) -;; (on-ordering-change [event] -;; (let [value (dom/event->value event) -;; value (read-string value)] -;; (st/emit! (di/update-opts :order value)))) -;; (on-clear [event] -;; (st/emit! (di/update-opts :filter "")))] -;; [:section.dashboard-bar.library-gap -;; [:div.dashboard-info - -;; ;; Counter -;; [:span.dashboard-images (tr "ds.num-images" (t/c icount))] - -;; ;; Sorting -;; [:div -;; [:span (tr "ds.ordering")] -;; [:select.input-select {:on-change on-ordering-change -;; :value (pr-str ordering)} -;; (for [[key value] (seq +ordering-options+)] -;; [:option {:key key :value (pr-str key)} (tr value)])]] - -;; ;; Search -;; [:form.dashboard-search -;; [:input.input-text {:key :images-search-box -;; :type "text" -;; :on-change on-term-change -;; :auto-focus true -;; :placeholder (tr "ds.search.placeholder") -;; :value filtering}] -;; [:div.clear-search {:on-click on-clear} i/close]]]]))) - -(def opts-iref - (-> (l/key :dashboard-images) - (l/derive st/state))) - -(mf/defc content - [{:keys [id type collection] :as props}] - (let [{:keys [selected] :as opts} (mf/deref opts-iref)] - [:section.dashboard-grid.library - (when collection - [:& grid-header {:collection collection}]) - (if collection - [:& grid {:id id :type type :collection collection :opts opts}] - [:span "EMPTY STATE TODO"]) - (when-not (empty? selected) - [:& grid-options {:id id :type type :selected selected}])])) - -;; --- Images Page - -(def collections-iref - (-> (l/key :images-collections) - (l/derive st/state))) - -(mf/defc images-page - [{:keys [id type] :as props}] - (let [collections (mf/deref collections-iref) - collections (cond->> (vals collections) - (= type :own) (filter #(= :own (:type %))) - (= type :builtin) (filter #(= :builtin (:type %))) - true (sort-by :created-at)) - - collection (cond - (uuid? id) (d/seek #(= id (:id %)) collections) - :else (first collections)) - id (:id collection)] - - (mf/use-effect #(st/emit! di/fetch-collections)) - (mf/use-effect - {:fn #(when id (st/emit! (di/initialize id))) - :deps (mf/deps id)}) - - [:section.dashboard-content - [:& nav {:type type :id id :collections collections}] - [:& content {:type type :id id :collection collection}]])) +;; ;; --- Page Title +;; +;; (mf/defc grid-header +;; [{:keys [collection] :as props}] +;; (let [{:keys [id type]} collection +;; on-change #(st/emit! (di/rename-collection id %)) +;; on-deleted #(st/emit! (rt/nav :dashboard-images nil {:type type})) +;; delete #(st/emit! (di/delete-collection id on-deleted)) +;; on-delete #(modal/show! confirm-dialog {:on-accept delete})] +;; [:& common/grid-header {:value (:name collection) +;; :on-change on-change +;; :on-delete on-delete}])) +;; +;; ;; --- Nav +;; +;; (mf/defc nav-item +;; [{:keys [coll selected?] :as props}] +;; (let [local (mf/use-state {}) +;; {:keys [id type name num-images]} coll +;; editable? (= type :own) +;; +;; on-click +;; (fn [event] +;; (let [type (or type :own)] +;; (st/emit! (rt/nav :dashboard-images {} {:type type :id id})))) +;; +;; on-cancel-edition #(swap! local dissoc :edit) +;; on-double-click #(when editable? (swap! local assoc :edit true)) +;; +;; on-input-keyup +;; (fn [event] +;; (when (kbd/enter? event) +;; (let [value (-> (dom/get-target event) +;; (dom/get-value) +;; (str/trim))] +;; (st/emit! (di/rename-collection id value)) +;; (swap! local assoc :edit false))))] +;; +;; [:li {:on-click on-click +;; :on-double-click on-double-click +;; :class-name (when selected? "current")} +;; (if (:edit @local) +;; [:div +;; [:input.element-title {:default-value name +;; :on-key-down on-input-keyup}] +;; [:span.close {:on-click on-cancel-edition} i/close]] +;; [:span.element-title (if id name "Storage")])])) +;; +;; (mf/defc nav +;; [{:keys [id type collections] :as props}] +;; (let [locale (i18n/use-locale) +;; own? (= type :own) +;; builtin? (= type :builtin) +;; create-collection #(st/emit! di/create-collection) +;; select-own-tab #(st/emit! (rt/nav :dashboard-images nil {:type :own})) +;; select-buitin-tab #(st/emit! (rt/nav :dashboard-images nil {:type :builtin}))] +;; [:div.library-bar +;; [:div.library-bar-inside +;; +;; ;; Tabs +;; [:ul.library-tabs +;; [:li {:class (when own? "current") +;; :on-click select-own-tab} +;; (t locale "ds.your-images-title")] +;; +;; [:li {:class (when builtin? "current") +;; :on-click select-buitin-tab} +;; (t locale "ds.store-images-title")]] +;; +;; ;; Collections List +;; [:ul.library-elements +;; (when own? +;; [:li +;; [:a.btn-primary {:on-click create-collection} +;; (t locale "ds.images-collection.new")]]) +;; +;; (for [item collections] +;; [:& nav-item {:coll item +;; :selected? (= (:id item) id) +;; :key (:id item)}])]]])) +;; +;; ;; --- Grid +;; +;; ;; (mf/defc grid-options-tooltip +;; ;; [{:keys [selected on-select title] :as props}] +;; ;; {:pre [(uuid? selected) +;; ;; (fn? on-select) +;; ;; (string? title)]} +;; ;; (let [colls (mf/deref collections-iref) +;; ;; colls (->> (vals colls) +;; ;; (filter #(= :own (:type %))) +;; ;; (remove #(= selected (:id %))) +;; ;; #_(sort-by :name colls)) +;; ;; on-select (fn [event id] +;; ;; (dom/prevent-default event) +;; ;; (dom/stop-propagation event) +;; ;; (on-select id))] +;; ;; [:ul.move-list +;; ;; [:li.title title] +;; ;; [:li +;; ;; (when (not (nil? selected)) +;; ;; [:a {:href "#" :on-click #(on-select % nil)} "Storage"])] +;; ;; (for [{:keys [id name] :as coll} colls] +;; ;; [:li {:key (pr-str id)} +;; ;; [:a {:on-click #(on-select % id)} name]])])) +;; +;; (mf/defc grid-options +;; [{:keys [id type selected] :as props}] +;; (let [local (mf/use-state {}) +;; delete #(st/emit! di/delete-selected) +;; on-delete #(modal/show! confirm-dialog {:on-accept delete}) +;; +;; ;; (on-toggle-copy [event] +;; ;; (swap! local update :show-copy-tooltip not)) +;; ;; (on-toggle-move [event] +;; ;; (swap! local update :show-move-tooltip not)) +;; ;; (on-copy [selected] +;; ;; (swap! local assoc +;; ;; :show-move-tooltip false +;; ;; :show-copy-tooltip false) +;; ;; (st/emit! (di/copy-selected selected))) +;; ;; (on-move [selected] +;; ;; (swap! local assoc +;; ;; :show-move-tooltip false +;; ;; :show-copy-tooltip false) +;; ;; (st/emit! (di/move-selected selected))) +;; ;; (on-rename [event] +;; ;; (let [selected (first selected)] +;; ;; (st/emit! (di/update-opts :edition selected)))) +;; ] +;; ;; MULTISELECT OPTIONS BAR +;; [:div.multiselect-bar +;; (when (= type :own) +;; ;; If editable +;; [:div.multiselect-nav +;; ;; [:span.move-item.tooltip.tooltip-top +;; ;; {:alt (tr "ds.multiselect-bar.copy") +;; ;; :on-click on-toggle-copy} +;; ;; (when (:show-copy-tooltip @local) +;; ;; [:& grid-options-tooltip {:selected id +;; ;; :title (tr "ds.multiselect-bar.copy-to-library") +;; ;; :on-select on-copy}]) +;; ;; i/copy] +;; ;; [:span.move-item.tooltip.tooltip-top +;; ;; {:alt (tr "ds.multiselect-bar.move") +;; ;; :on-click on-toggle-move} +;; ;; (when (:show-move-tooltip @local) +;; ;; [:& grid-options-tooltip {:selected id +;; ;; :title (tr "ds.multiselect-bar.move-to-library") +;; ;; :on-select on-move}]) +;; ;; i/move] +;; ;; (when (= 1 (count selected)) +;; ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.rename") +;; ;; :on-click on-rename} +;; ;; i/pencil]) +;; [:span.delete.tooltip.tooltip-top +;; {:alt (tr "ds.multiselect-bar.delete") +;; :on-click on-delete} +;; i/trash]] +;; +;; ;; If not editable +;; ;; [:div.multiselect-nav +;; ;; [:span.move-item.tooltip.tooltip-top {:alt (tr "ds.multiselect-bar.copy") +;; ;; :on-click on-toggle-copy} +;; ;; (when (:show-copy-tooltip @local) +;; ;; [:& grid-options-tooltip {:selected id +;; ;; :title (tr "ds.multiselect-bar.copy-to-library") +;; ;; :on-select on-copy}]) +;; ;; i/organize]] +;; )])) +;; +;; +;; ;; --- Grid Form +;; +;; (mf/defc grid-form +;; [{:keys [id type uploading?] :as props}] +;; (let [input (mf/use-ref nil) +;; on-click #(dom/click (mf/ref-node input)) +;; on-select #(st/emit! (->> (dom/get-target %) +;; (dom/get-files) +;; (array-seq) +;; (di/create-images id)))] +;; [:div.grid-item.add-project {:on-click on-click} +;; (if uploading? +;; [:div i/loader-pencil] +;; [:span (tr "ds.image-new")]) +;; [:input.upload-image-input +;; {:style {:display "none"} +;; :multiple true +;; :ref input +;; :value "" +;; :accept "image/jpeg,image/png,image/webp" +;; :type "file" +;; :on-change on-select}]])) +;; +;; ;; --- Grid Item +;; +;; (mf/defc grid-item +;; [{:keys [image selected? edition?] :as props}] +;; (let [toggle-selection #(st/emit! (if selected? +;; (di/deselect-image (:id image)) +;; (di/select-image (:id image)))) +;; on-blur +;; (fn [event] +;; (let [target (dom/get-target event) +;; name (dom/get-value target)] +;; (st/emit! (di/update-opts :edition false) +;; (di/rename-image (:id image) name)))) +;; +;; on-key-down +;; (fn [event] +;; (when (kbd/enter? event) +;; (on-blur event))) +;; +;; on-edit +;; (fn [event] +;; (dom/stop-propagation event) +;; (dom/prevent-default event) +;; (st/emit! (di/update-opts :edition (:id image)))) +;; +;; background (str "url('" (:thumb-uri image) "')")] +;; +;; [:div.grid-item.images-th +;; [:div.grid-item-th {:style {:background-image background}} +;; [:div.input-checkbox.check-primary +;; [:input {:type "checkbox" +;; :id (:id image) +;; :on-change toggle-selection +;; :checked selected?}] +;; [:label {:for (:id image)}]]] +;; +;; [:div.item-info +;; (if edition? +;; [:input.element-name {:type "text" +;; :auto-focus true +;; :on-key-down on-key-down +;; :on-blur on-blur +;; :on-click on-edit +;; :default-value (:name image)}] +;; [:h3 {:on-double-click on-edit} (:name image)]) +;; [:span.date (tr "ds.uploaded-at" (dt/format (:created-at image) "dd/MM/yyyy"))]]])) +;; +;; ;; --- Grid +;; +;; ;; (defn- make-images-iref +;; ;; [collection-id] +;; ;; (letfn [(selector [state] +;; ;; (->> (vals (:images state)) +;; ;; (filterv #(= (:collection-id %) collection-id))))] +;; ;; (-> (l/lens selector) +;; ;; (l/derive st/state)))) +;; +;; (def images-iref +;; (-> (comp (l/key :images) (l/lens vals)) +;; (l/derive st/state))) +;; +;; (mf/defc grid +;; [{:keys [id type collection opts] :as props}] +;; (let [editable? (= type :own) +;; ;; images-iref (mf/use-memo {:fn #(make-images-iref id) +;; ;; :deps (mf/deps id)}) +;; images (->> (mf/deref images-iref) +;; (sort-by :created-at))] +;; [:div.dashboard-grid-content +;; [:div.dashboard-grid-row +;; (when editable? +;; [:& grid-form {:id id :type type :uploading? (:uploading opts)}]) +;; (for [item images] +;; [:& grid-item {:image item +;; :key (:id item) +;; :selected? (contains? (:selected opts) (:id item)) +;; :edition? (= (:edition opts) (:id item))}])]])) +;; +;; ;; --- Menu +;; +;; ;; (mf/defc menu +;; ;; [{:keys [opts coll] :as props}] +;; ;; (let [ordering (:order opts :name) +;; ;; filtering (:filter opts "") +;; ;; icount (count (:images coll))] +;; ;; (letfn [(on-term-change [event] +;; ;; (let [term (-> (dom/get-target event) +;; ;; (dom/get-value))] +;; ;; (st/emit! (di/update-opts :filter term)))) +;; ;; (on-ordering-change [event] +;; ;; (let [value (dom/event->value event) +;; ;; value (read-string value)] +;; ;; (st/emit! (di/update-opts :order value)))) +;; ;; (on-clear [event] +;; ;; (st/emit! (di/update-opts :filter "")))] +;; ;; [:section.dashboard-bar.library-gap +;; ;; [:div.dashboard-info +;; +;; ;; ;; Counter +;; ;; [:span.dashboard-images (tr "ds.num-images" (t/c icount))] +;; +;; ;; ;; Sorting +;; ;; [:div +;; ;; [:span (tr "ds.ordering")] +;; ;; [:select.input-select {:on-change on-ordering-change +;; ;; :value (pr-str ordering)} +;; ;; (for [[key value] (seq +ordering-options+)] +;; ;; [:option {:key key :value (pr-str key)} (tr value)])]] +;; +;; ;; ;; Search +;; ;; [:form.dashboard-search +;; ;; [:input.input-text {:key :images-search-box +;; ;; :type "text" +;; ;; :on-change on-term-change +;; ;; :auto-focus true +;; ;; :placeholder (tr "ds.search.placeholder") +;; ;; :value filtering}] +;; ;; [:div.clear-search {:on-click on-clear} i/close]]]]))) +;; +;; (def opts-iref +;; (-> (l/key :dashboard-images) +;; (l/derive st/state))) +;; +;; (mf/defc content +;; [{:keys [id type collection] :as props}] +;; (let [{:keys [selected] :as opts} (mf/deref opts-iref)] +;; [:section.dashboard-grid.library +;; (when collection +;; [:& grid-header {:collection collection}]) +;; (if collection +;; [:& grid {:id id :type type :collection collection :opts opts}] +;; [:span "EMPTY STATE TODO"]) +;; (when-not (empty? selected) +;; [:& grid-options {:id id :type type :selected selected}])])) +;; +;; ;; --- Images Page +;; +;; (def collections-iref +;; (-> (l/key :images-collections) +;; (l/derive st/state))) +;; +;; (mf/defc images-page +;; [{:keys [id type] :as props}] +;; (let [collections (mf/deref collections-iref) +;; collections (cond->> (vals collections) +;; (= type :own) (filter #(= :own (:type %))) +;; (= type :builtin) (filter #(= :builtin (:type %))) +;; true (sort-by :created-at)) +;; +;; collection (cond +;; (uuid? id) (d/seek #(= id (:id %)) collections) +;; :else (first collections)) +;; id (:id collection)] +;; +;; (mf/use-effect #(st/emit! di/fetch-collections)) +;; (mf/use-effect +;; {:fn #(when id (st/emit! (di/initialize id))) +;; :deps (mf/deps id)}) +;; +;; [:section.dashboard-content +;; [:& nav {:type type :id id :collections collections}] +;; [:& content {:type type :id id :collection collection}]])) diff --git a/frontend/src/uxbox/main/ui/dashboard/library.cljs b/frontend/src/uxbox/main/ui/dashboard/library.cljs index da4f9eaf37..dd9ef5227c 100644 --- a/frontend/src/uxbox/main/ui/dashboard/library.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/library.cljs @@ -16,13 +16,77 @@ [uxbox.util.router :as rt] [uxbox.util.i18n :as i18n :refer [t tr]] [uxbox.util.color :as uc] + [uxbox.util.dom :as dom] [uxbox.main.data.icons :as dico] [uxbox.main.data.images :as dimg] [uxbox.main.data.colors :as dcol] [uxbox.builtins.icons :as i] [uxbox.main.store :as st] [uxbox.main.refs :as refs] - [uxbox.main.ui.dashboard.components.context-menu :refer [context-menu]])) + [uxbox.main.ui.components.context-menu :refer [context-menu]] + [uxbox.main.ui.modal :as modal] + [uxbox.main.ui.confirm :refer [confirm-dialog]] + [uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]] + )) + +(mf/defc modal-create-color + [{:keys [on-accept on-cancel] :as ctx}] + (let [state (mf/use-state { :current-color "#406280" })] + (letfn [(accept [event] + (dom/prevent-default event) + (modal/hide!) + (when on-accept (on-accept (:current-color @state)))) + + (cancel [event] + (dom/prevent-default event) + (modal/hide!) + (when on-cancel (on-cancel)))] + [:div.modal-create-color + [:h3.modal-create-color-title (tr "modal.create-color.new-color")] + [:& colorpicker {:value (:current-color @state) + :colors (into-array @most-used-colors) + :on-change #(swap! state assoc :current-color %)}] + + [:input.btn-primary {:type "button" + :value (tr "ds.button.save") + :on-click accept}] + + [:a.close {:href "#" :on-click cancel} i/close]]))) + + +(defmulti create-library (fn [x _] x)) +(defmethod create-library :icons [_ team-id] + (let [name (str "Icon Library "(gensym "l"))] + (st/emit! (dico/create-icon-library team-id name)))) + +(defmethod create-library :images [_ team-id] + (let [name (str "Image Library "(gensym "l"))] + (st/emit! (dimg/create-image-library team-id name)))) + +(defmethod create-library :palettes [_ team-id] + (let [name (str "Image Library "(gensym "l"))] + (st/emit! (dcol/create-color-library team-id name)))) + +(defmulti create-item (fn [x _ _] x)) + +(defmethod create-item :icons [_ library-id data] + (let [files (->> data + (dom/get-target) + (dom/get-files) + (array-seq))] + (st/emit! (dico/create-icons library-id files)))) + +(defmethod create-item :images [_ library-id data] + (let [files (->> data + (dom/get-target) + (dom/get-files) + (array-seq))] + (st/emit! (dimg/create-images library-id files)))) + +(defmethod create-item :palettes [_ library-id] + (letfn [(dispatch-color [color] + (st/emit! (dcol/create-color library-id color)))] + (modal/show! modal-create-color {:on-accept dispatch-color}))) (mf/defc library-header [{:keys [section team-id] :as props}] @@ -51,22 +115,23 @@ (let [locale (i18n/use-locale)] [:aside.library-sidebar [:button.library-sidebar-add-item - {:type "button"} + {:type "button" + :on-click #(create-library section team-id)} (t locale (str "dashboard.library.add-library." (name section)))] [:ul.library-sidebar-list (for [item items] [:li.library-sidebar-list-element {:key (:id item) :class-name (when (= library-id (:id item)) "current") - :on-click (fn [] (let [path (keyword (str "dashboard-library-" (name section))) - route (rt/nav path {:team-id team-id - :library-id (:id item)})] - (dico/fetch-icon-library (:id item)) - (st/emit! route)))} + :on-click + (fn [] + (let [path (keyword (str "dashboard-library-" (name section)))] + (dico/fetch-icon-library (:id item)) + (st/emit! (rt/nav path {:team-id team-id :library-id (:id item)}))))} [:a (:name item)]])]])) (mf/defc library-top-menu - [{:keys [selected section]}] + [{:keys [selected section library-id]}] (let [state (mf/use-state {:is-open false}) locale (i18n/use-locale)] [:header.library-top-menu @@ -75,13 +140,27 @@ [:a.library-top-menu-current-action { :on-click #(swap! state update :is-open not)} [:span i/arrow-down]] - [:& context-menu {:is-open (:is-open @state) - :options [[(t locale "ds.button.rename") #(println "Rename")] - [(t locale "ds.button.delete") #(println "Delete")]]}]] + [:& context-menu + {:show (:is-open @state) + :on-close #(swap! state update :is-open not) + :options [[(t locale "ds.button.rename") #(println "Rename")] + [(t locale "ds.button.delete") #(println "Delete")]]}]] [:div.library-top-menu-actions [:a i/trash] - [:a.btn-dashboard (t locale (str "dashboard.library.add-item." (name section)))]]])) + + (if (= section :palettes) + [:button.btn-dashboard + {:on-click #(create-item section library-id)} + (t locale (str "dashboard.library.add-item." (name section)))] + + [:* + [:label {:for "file-upload" :class-name "btn-dashboard"} + (t locale (str "dashboard.library.add-item." (name section)))] + [:input {:on-change #(create-item section library-id %) + :id "file-upload" :type "file" :style {:display "none"}}]] + + )]])) (mf/defc library-icon-card [{:keys [id name url content metadata]}] @@ -107,11 +186,13 @@ [:div.library-card-footer-menu { :on-click #(swap! state update :is-open not) } i/actions] - [:& context-menu {:is-open (:is-open @state) - :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) + [:& context-menu + {:show (:is-open @state) + :on-close #(swap! state update :is-open not) + :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) (mf/defc library-image-card - [{:keys [id name url]}] + [{:keys [id name thumb-uri]}] (let [locale (i18n/use-locale) state (mf/use-state {:is-open false})] [:div.library-card.library-image @@ -122,39 +203,44 @@ #_(:checked false)}] [:label {:for (str "image-" id)}]] [:div.library-card-image - [:img {:src url}]] + [:img {:src thumb-uri}]] [:div.library-card-footer [:div.library-card-footer-name name] [:div.library-card-footer-timestamp "Less than 5 seconds ago"] [:div.library-card-footer-menu { :on-click #(swap! state update :is-open not) } i/actions] - [:& context-menu {:is-open (:is-open @state) - :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) + [:& context-menu + {:show (:is-open @state) + :on-close #(swap! state update :is-open not) + :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) (mf/defc library-color-card [{ :keys [ id content ] }] - (let [locale (i18n/use-locale) - state (mf/use-state {:is-open false})] - [:div.library-card.library-color - [:div.input-checkbox.check-primary - [:input {:type "checkbox" - :id (str "color-" id) - :on-change #(println "toggle-selection") - #_(:checked false)}] - [:label {:for (str "color-" id)}]] - [:div.library-card-image - { :style { :background-color content }}] - [:div.library-card-footer - [:div.library-card-footer-name content ] - [:div.library-card-footer-color - [:span.library-card-footer-color-label "RGB"] - [:span.library-card-footer-color-rgb (str/join " " (uc/hex->rgb content))]] - [:div.library-card-footer-menu - { :on-click #(swap! state update :is-open not) } - i/actions] - [:& context-menu {:is-open (:is-open @state) - :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]])) + (when content + (let [locale (i18n/use-locale) + state (mf/use-state {:is-open false})] + [:div.library-card.library-color + [:div.input-checkbox.check-primary + [:input {:type "checkbox" + :id (str "color-" id) + :on-change #(println "toggle-selection") + #_(:checked false)}] + [:label {:for (str "color-" id)}]] + [:div.library-card-image + { :style { :background-color content }}] + [:div.library-card-footer + [:div.library-card-footer-name content ] + [:div.library-card-footer-color + [:span.library-card-footer-color-label "RGB"] + [:span.library-card-footer-color-rgb (str/join " " (uc/hex->rgb content))]] + [:div.library-card-footer-menu + { :on-click #(swap! state update :is-open not) } + i/actions] + [:& context-menu + {:show (:is-open @state) + :on-close #(swap! state update :is-open not) + :options [[(t locale "ds.button.delete") #(println "Delete")]]}]]]))) (def icon-libraries-ref (-> (comp (l/key :library) (l/key :icon-libraries)) @@ -197,11 +283,11 @@ (when library-id [:section.library-content - [:& library-top-menu {:selected selected-library :section section}] + [:& library-top-menu {:selected selected-library :section section :library-id library-id}] [:div.library-page-cards-container (for [item items] (let [item (assoc item :key (:id item))] (case section :icons [:& library-icon-card item] - :images [:& library-image-card { :name "Nicolas Cage" :url "https://www.placecage.com/200/200" }] + :images [:& library-image-card item] :palettes [:& library-color-card item ])))]])])) diff --git a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs index 899582eb9c..19ffa6cfe9 100644 --- a/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs +++ b/frontend/src/uxbox/main/ui/workspace/colorpalette.cljs @@ -125,5 +125,5 @@ (mf/defc colorpalette [props] (let [colls (mf/deref collections-iref)] - (mf/use-effect #(st/emit! (udc/fetch-collections))) + #_(mf/use-effect #(st/emit! (udc/fetch-collections))) [:& palette {:colls (vals colls)}])) diff --git a/frontend/src/uxbox/main/ui/workspace/images.cljs b/frontend/src/uxbox/main/ui/workspace/images.cljs index d2cc8b460d..266d97ef38 100644 --- a/frontend/src/uxbox/main/ui/workspace/images.cljs +++ b/frontend/src/uxbox/main/ui/workspace/images.cljs @@ -160,8 +160,8 @@ (d/read-string) (swap! local assoc :collection-id))] - (mf/use-effect #(st/emit! udi/fetch-collections)) - (mf/use-effect + #_(mf/use-effect #(st/emit! udi/fetch-collections)) + #_(mf/use-effect {:deps (mf/deps collection-id) :fn #(when collection-id (st/emit! (udi/fetch-images collection-id)))}) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs index 1a843f4abc..6ee964c52f 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/icons.cljs @@ -46,7 +46,7 @@ (fn [event data] (st/emit! (dw/select-for-drawing :icon data)))] - (mf/use-effect + #_(mf/use-effect {:fn #(st/emit! (di/fetch-icons collection-id)) :deps (mf/deps collection-id)}) @@ -79,7 +79,7 @@ (st/emit! (dw/select-for-drawing nil)) (reset! selected val))] - (mf/use-effect + #_(mf/use-effect {:fn #(st/emit! di/fetch-collections)}) [:div#form-figures.tool-window