mirror of
https://github.com/penpot/penpot.git
synced 2025-07-31 17:28:20 +02:00
🎉 Link files to libraries
This commit is contained in:
parent
5ff0a723d5
commit
752038039c
22 changed files with 1080 additions and 318 deletions
|
@ -41,7 +41,7 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-colors-library (:id color)] color)
|
||||
(update-in [:workspace-file :colors] #(conj % color))
|
||||
(assoc-in [:workspace-local :color-for-rename] (:id color))))))
|
||||
|
||||
(def clear-color-for-rename
|
||||
|
@ -67,7 +67,7 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-colors-library (:id color)] color)))))
|
||||
(update-in [:workspace-file :colors] #(d/replace-by-id % color))))))
|
||||
|
||||
(declare update-color-result)
|
||||
|
||||
|
@ -86,7 +86,7 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-colors-library (:id color)] color)))))
|
||||
(update-in [:workspace-file :colors] #(d/replace-by-id % color))))))
|
||||
|
||||
(declare delete-color-result)
|
||||
|
||||
|
@ -104,5 +104,6 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(d/dissoc-in [:workspace-colors-library color-id])))))
|
||||
(update-in [:workspace-file :colors]
|
||||
(fn [colors] (filter #(not= (:id %) color-id) colors)))))))
|
||||
|
||||
|
|
|
@ -1433,8 +1433,9 @@
|
|||
;; Persistence
|
||||
|
||||
(def set-file-shared dwp/set-file-shared)
|
||||
(def fetch-media-library dwp/fetch-media-library)
|
||||
(def fetch-colors-library dwp/fetch-colors-library)
|
||||
(def fetch-shared-files dwp/fetch-shared-files)
|
||||
(def link-file-to-library dwp/link-file-to-library)
|
||||
(def unlink-file-from-library dwp/unlink-file-from-library)
|
||||
(def add-media-object-from-url dwp/add-media-object-from-url)
|
||||
(def upload-media-objects dwp/upload-media-objects)
|
||||
(def delete-media-object dwp/delete-media-object)
|
||||
|
|
|
@ -137,6 +137,7 @@
|
|||
::ordering
|
||||
::data]))
|
||||
|
||||
(declare fetch-libraries-content)
|
||||
(declare bundle-fetched)
|
||||
|
||||
(defn- fetch-bundle
|
||||
|
@ -147,10 +148,18 @@
|
|||
(->> (rx/zip (rp/query :file {:id file-id})
|
||||
(rp/query :file-users {:id file-id})
|
||||
(rp/query :project-by-id {:project-id project-id})
|
||||
(rp/query :pages {:file-id file-id}))
|
||||
(rp/query :pages {:file-id file-id})
|
||||
(rp/query :media-objects {:file-id file-id :is-local false})
|
||||
(rp/query :colors {:file-id file-id})
|
||||
(rp/query :file-libraries {:file-id file-id}))
|
||||
(rx/first)
|
||||
(rx/map (fn [[file users project pages]]
|
||||
(bundle-fetched file users project pages)))
|
||||
(rx/mapcat
|
||||
(fn [bundle]
|
||||
(->> (fetch-libraries-content (get bundle 6))
|
||||
(rx/map (fn [[lib-media-objects lib-colors]]
|
||||
(conj bundle lib-media-objects lib-colors))))))
|
||||
(rx/map (fn [bundle]
|
||||
(apply bundle-fetched bundle)))
|
||||
(rx/catch (fn [{:keys [type code] :as error}]
|
||||
(cond
|
||||
(= :not-found type)
|
||||
|
@ -163,27 +172,78 @@
|
|||
:else
|
||||
(throw error))))))))
|
||||
|
||||
(defn- fetch-libraries-content
|
||||
[libraries]
|
||||
(if (empty? libraries)
|
||||
(rx/of [{} {}])
|
||||
(rx/zip
|
||||
(->> ;; fetch media-objects list of each library, and concatenate in a sequence
|
||||
(apply rx/zip (for [library libraries]
|
||||
(->> (rp/query :media-objects {:file-id (:id library)
|
||||
:is-local false})
|
||||
(rx/map (fn [media-objects]
|
||||
[(:id library) media-objects])))))
|
||||
|
||||
;; reorganize the sequence as a map {library-id -> media-objects}
|
||||
(rx/map (fn [media-list]
|
||||
(reduce (fn [result, [library-id media-objects]]
|
||||
(assoc result library-id media-objects))
|
||||
{}
|
||||
media-list))))
|
||||
|
||||
(->> ;; fetch colorss list of each library, and concatenate in a vector
|
||||
(apply rx/zip (for [library libraries]
|
||||
(->> (rp/query :colors {:file-id (:id library)})
|
||||
(rx/map (fn [colors]
|
||||
[(:id library) colors])))))
|
||||
|
||||
;; reorganize the sequence as a map {library-id -> colors}
|
||||
(rx/map (fn [colors-list]
|
||||
(reduce (fn [result, [library-id colors]]
|
||||
(assoc result library-id colors))
|
||||
{}
|
||||
colors-list)))))))
|
||||
|
||||
(defn- bundle-fetched
|
||||
[file users project pages]
|
||||
[file users project pages media-objects colors libraries lib-media-objects lib-colors]
|
||||
(ptk/reify ::bundle-fetched
|
||||
IDeref
|
||||
(-deref [_]
|
||||
{:file file
|
||||
:users users
|
||||
:project project
|
||||
:pages pages})
|
||||
:pages pages
|
||||
:media-objects media-objects
|
||||
:colors colors
|
||||
:libraries libraries})
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [assoc-page #(assoc-in %1 [:workspace-pages (:id %2)] %2)]
|
||||
(let [assoc-page
|
||||
#(assoc-in %1 [:workspace-pages (:id %2)] %2)
|
||||
|
||||
assoc-media-objects
|
||||
#(assoc-in %1 [:workspace-libraries %2 :media-objects]
|
||||
(get lib-media-objects %2))
|
||||
|
||||
assoc-colors
|
||||
#(assoc-in %1 [:workspace-libraries %2 :colors]
|
||||
(get lib-colors %2))]
|
||||
|
||||
(as-> state $$
|
||||
(assoc $$
|
||||
:workspace-file file
|
||||
:workspace-file (assoc file
|
||||
:media-objects media-objects
|
||||
:colors colors)
|
||||
:workspace-users (d/index-by :id users)
|
||||
:workspace-pages {}
|
||||
:workspace-project project)
|
||||
:workspace-project project
|
||||
:workspace-libraries (d/index-by :id libraries))
|
||||
(reduce assoc-media-objects $$ (keys lib-media-objects))
|
||||
(reduce assoc-colors $$ (keys lib-colors))
|
||||
(reduce assoc-page $$ pages))))))
|
||||
|
||||
|
||||
;; --- Set File shared
|
||||
|
||||
(defn set-file-shared
|
||||
|
@ -200,6 +260,79 @@
|
|||
(->> (rp/mutation :set-file-shared params)
|
||||
(rx/ignore))))))
|
||||
|
||||
|
||||
;; --- Fetch Shared Files
|
||||
|
||||
(declare shared-files-fetched)
|
||||
|
||||
(defn fetch-shared-files
|
||||
[]
|
||||
(ptk/reify ::fetch-shared-files
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [params {}]
|
||||
(->> (rp/query :shared-files params)
|
||||
(rx/map shared-files-fetched))))))
|
||||
|
||||
(defn shared-files-fetched
|
||||
[files]
|
||||
(us/verify (s/every ::file) files)
|
||||
(ptk/reify ::shared-files-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [state (dissoc state :files)]
|
||||
(assoc state :workspace-shared-files files)))))
|
||||
|
||||
|
||||
;; --- Link and unlink Files
|
||||
|
||||
(declare file-linked)
|
||||
|
||||
(defn link-file-to-library
|
||||
[file-id library-id]
|
||||
(ptk/reify ::link-file-to-library
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [params {:file-id file-id
|
||||
:library-id library-id}]
|
||||
(->> (->> (rp/mutation :link-file-to-library params)
|
||||
(rx/mapcat
|
||||
#(rx/zip (rp/query :file-library {:file-id library-id})
|
||||
(rp/query :media-objects {:file-id library-id
|
||||
:is-local false})
|
||||
(rp/query :colors {:file-id library-id}))))
|
||||
(rx/map file-linked))))))
|
||||
|
||||
(defn file-linked
|
||||
[[library media-objects colors]]
|
||||
(ptk/reify ::file-linked
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:workspace-libraries (:id library)]
|
||||
(assoc library
|
||||
:media-objects media-objects
|
||||
:colors colors)))))
|
||||
|
||||
(declare file-unlinked)
|
||||
|
||||
(defn unlink-file-from-library
|
||||
[file-id library-id]
|
||||
(ptk/reify ::unlink-file-from-library
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [params {:file-id file-id
|
||||
:library-id library-id}]
|
||||
(->> (rp/mutation :unlink-file-from-library params)
|
||||
(rx/map #(file-unlinked file-id library-id)))))))
|
||||
|
||||
(defn file-unlinked
|
||||
[file-id library-id]
|
||||
(ptk/reify ::file-unlinked
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(d/dissoc-in state [:workspace-libraries library-id]))))
|
||||
|
||||
|
||||
;; --- Fetch Pages
|
||||
|
||||
(declare page-fetched)
|
||||
|
@ -298,46 +431,6 @@
|
|||
(rx/of go-to-file)
|
||||
(rx/empty))))))))))
|
||||
|
||||
;; --- Fetch Workspace Media library
|
||||
|
||||
(declare media-library-fetched)
|
||||
|
||||
(defn fetch-media-library
|
||||
[file-id]
|
||||
(ptk/reify ::fetch-media-library
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/query :media-objects {:file-id file-id :is-local false})
|
||||
(rx/map media-library-fetched)))))
|
||||
|
||||
(defn media-library-fetched
|
||||
[media-objects]
|
||||
(ptk/reify ::media-library-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [media-objects (d/index-by :id media-objects)]
|
||||
(assoc state :workspace-media-library media-objects)))))
|
||||
|
||||
;; --- Fetch Workspace Colors library
|
||||
|
||||
(declare colors-library-fetched)
|
||||
|
||||
(defn fetch-colors-library
|
||||
[file-id]
|
||||
(ptk/reify ::fetch-colors-library
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(->> (rp/query :colors {:file-id file-id})
|
||||
(rx/map colors-library-fetched)))))
|
||||
|
||||
(defn colors-library-fetched
|
||||
[colors]
|
||||
(ptk/reify ::colors-library-fetched
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [colors (d/index-by :id colors)]
|
||||
(assoc state :workspace-colors-library colors)))))
|
||||
|
||||
|
||||
;; --- Upload local media objects
|
||||
|
||||
|
@ -357,6 +450,8 @@
|
|||
|
||||
on-error #(do (di/notify-finished-loading)
|
||||
(di/process-error %))
|
||||
|
||||
is-library (not= file-id (:id (:workspace-file state)))
|
||||
|
||||
prepare
|
||||
(fn [url]
|
||||
|
@ -370,7 +465,7 @@
|
|||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/mutation! :add-media-object-from-url %))
|
||||
(rx/do on-success)
|
||||
(rx/map (partial upload-media-objects-result file-id is-local))
|
||||
(rx/map (partial upload-media-objects-result file-id is-local is-library))
|
||||
(rx/catch on-error)))))))
|
||||
|
||||
(defn upload-media-objects
|
||||
|
@ -389,6 +484,8 @@
|
|||
on-error #(do (di/notify-finished-loading)
|
||||
(di/process-error %))
|
||||
|
||||
is-library (not= file-id (:id (:workspace-file state)))
|
||||
|
||||
prepare
|
||||
(fn [js-file]
|
||||
{:name (.-name js-file)
|
||||
|
@ -403,32 +500,43 @@
|
|||
(rx/map prepare)
|
||||
(rx/mapcat #(rp/mutation! :upload-media-object %))
|
||||
(rx/do on-success)
|
||||
(rx/map (partial upload-media-objects-result file-id is-local))
|
||||
(rx/map (partial upload-media-objects-result file-id is-local is-library))
|
||||
(rx/catch on-error)))))))
|
||||
|
||||
(defn upload-media-objects-result
|
||||
[file-id is-local media-object]
|
||||
[file-id is-local is-library media-object]
|
||||
(us/verify ::us/uuid file-id)
|
||||
(us/verify ::us/boolean is-local)
|
||||
(us/verify ::cm/media-object media-object)
|
||||
(ptk/reify ::upload-media-objects-result
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if is-local
|
||||
(if is-local ;; the media-object is local to the file, not for its library
|
||||
state
|
||||
(assoc-in state
|
||||
[:workspace-media-library (:id media-object)]
|
||||
media-object)))))
|
||||
(if is-library ;; the file is not the currently editing one, but a linked shared file
|
||||
(update-in state
|
||||
[:workspace-libraries file-id :media-objects]
|
||||
#(conj % media-object))
|
||||
(update-in state
|
||||
[:workspace-file :media-objects]
|
||||
#(conj % media-object)))))))
|
||||
|
||||
|
||||
;; --- Delete media object
|
||||
|
||||
(defn delete-media-object
|
||||
[id]
|
||||
[file-id id]
|
||||
(ptk/reify ::delete-media-object
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update state :workspace-media-library dissoc id))
|
||||
(let [is-library (not= file-id (:id (:workspace-file state)))]
|
||||
(if is-library
|
||||
(update-in state
|
||||
[:workspace-libraries file-id :media-objects]
|
||||
(fn [media-objects] (filter #(not= (:id %) id) media-objects)))
|
||||
(update-in state
|
||||
[:workspace-file :media-objects]
|
||||
(fn [media-objects] (filter #(not= (:id %) id) media-objects))))))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
|
|
|
@ -57,11 +57,11 @@
|
|||
(def workspace-project
|
||||
(l/derived :workspace-project st/state))
|
||||
|
||||
(def workspace-media-library
|
||||
(l/derived :workspace-media-library st/state))
|
||||
(def workspace-shared-files
|
||||
(l/derived :workspace-shared-files st/state))
|
||||
|
||||
(def workspace-colors-library
|
||||
(l/derived :workspace-colors-library st/state))
|
||||
(def workspace-libraries
|
||||
(l/derived :workspace-libraries st/state))
|
||||
|
||||
(def workspace-users
|
||||
(l/derived :workspace-users st/state))
|
||||
|
|
|
@ -56,6 +56,7 @@
|
|||
(def layers (icon-xref :layers))
|
||||
(def letter-spacing (icon-xref :letter-spacing))
|
||||
(def library (icon-xref :library))
|
||||
(def libraries (icon-xref :libraries))
|
||||
(def line (icon-xref :line))
|
||||
(def line-height (icon-xref :line-height))
|
||||
(def loader (icon-xref :loader))
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.streams :as ms]
|
||||
[uxbox.main.ui.confirm]
|
||||
[uxbox.main.ui.keyboard :as kbd]
|
||||
[uxbox.main.ui.hooks :as hooks]
|
||||
[uxbox.main.ui.workspace.viewport :refer [viewport coordinates]]
|
||||
|
|
155
frontend/src/uxbox/main/ui/workspace/libraries.cljs
Normal file
155
frontend/src/uxbox/main/ui/workspace/libraries.cljs
Normal file
|
@ -0,0 +1,155 @@
|
|||
;; 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/.
|
||||
;;
|
||||
;; Copyright (c) 2016 Andrey Antukh <niwi@niwi.nz>
|
||||
;; Copyright (c) 2016 Juan de la Cruz <delacruzgarciajuan@gmail.com>
|
||||
|
||||
(ns uxbox.main.ui.workspace.libraries
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[cuerdas.core :as str]
|
||||
[uxbox.util.dom :as dom]
|
||||
[uxbox.util.i18n :refer (tr)]
|
||||
[uxbox.util.data :refer [classnames matches-search]]
|
||||
[uxbox.main.store :as st]
|
||||
[uxbox.main.refs :as refs]
|
||||
[uxbox.main.data.workspace :as dw]
|
||||
[uxbox.main.ui.icons :as i]
|
||||
[uxbox.main.ui.modal :as modal]))
|
||||
|
||||
(mf/defc libraries-tab
|
||||
[{:keys [file libraries shared-files] :as props}]
|
||||
(let [state (mf/use-state {:search-term ""})
|
||||
|
||||
sorted-libraries (->> (vals libraries)
|
||||
(sort-by #(str/lower (:name %))))
|
||||
|
||||
filtered-files (->> shared-files
|
||||
(filter #(not= (:id %) (:id file)))
|
||||
(filter #(nil? (get libraries (:id %))))
|
||||
(filter #(matches-search (:name %) (:search-term @state)))
|
||||
(sort-by #(str/lower (:name %))))
|
||||
|
||||
on-search-term-change (fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
(dom/get-value))]
|
||||
(swap! state assoc :search-term value)))
|
||||
|
||||
on-search-clear-click (fn [event]
|
||||
(swap! state assoc :search-term ""))
|
||||
|
||||
link-library (fn [library-id]
|
||||
(st/emit! (dw/link-file-to-library (:id file) library-id)))
|
||||
|
||||
unlink-library (fn [library-id]
|
||||
(st/emit! (dw/unlink-file-from-library (:id file) library-id)))
|
||||
|
||||
contents-str (fn [library graphics-count colors-count]
|
||||
(str
|
||||
(str/join " · "
|
||||
(cond-> []
|
||||
(< 0 graphics-count)
|
||||
(conj (tr "workspace.libraries.graphics" graphics-count))
|
||||
|
||||
(< 0 colors-count)
|
||||
(conj (tr "workspace.libraries.colors" colors-count))))
|
||||
"\u00A0"))] ;; Include a so this block has always some content
|
||||
[:*
|
||||
[:div.section
|
||||
[:div.section-title (tr "workspace.libraries.in-this-file")]
|
||||
[:div.section-list
|
||||
[:div.section-list-item
|
||||
[:div.item-name (tr "workspace.libraries.file-library")]
|
||||
[:div.item-contents (contents-str file
|
||||
(count (:media-objects file))
|
||||
(count (:colors file)))]]
|
||||
(for [library sorted-libraries]
|
||||
[:div.section-list-item {:key (:id library)}
|
||||
[:div.item-name (:name library)]
|
||||
[:div.item-contents (contents-str library
|
||||
(count (:media-objects library))
|
||||
(count (:colors library)))]
|
||||
[:input.item-button {:type "button"
|
||||
:value (tr "workspace.libraries.remove")
|
||||
:on-click #(unlink-library (:id library))}]])
|
||||
]]
|
||||
[:div.section
|
||||
[:div.section-title (tr "workspace.libraries.shared-libraries")]
|
||||
[:div.libraries-search
|
||||
[:input.search-input
|
||||
{:placeholder (tr "workspace.libraries.search-shared-libraries")
|
||||
:type "text"
|
||||
:value (:search-term @state)
|
||||
:on-change on-search-term-change}]
|
||||
(if (str/empty? (:search-term @state))
|
||||
[:div.search-icon
|
||||
i/search]
|
||||
[:div.search-icon.search-close
|
||||
{:on-click on-search-clear-click}
|
||||
i/close])]
|
||||
(if (> (count filtered-files) 0)
|
||||
[:div.section-list
|
||||
(for [file filtered-files]
|
||||
[:div.section-list-item {:key (:id file)}
|
||||
[:div.item-name (:name file)]
|
||||
[:div.item-contents (contents-str file
|
||||
(:graphics-count file)
|
||||
(:colors-count file))]
|
||||
[:input.item-button {:type "button"
|
||||
:value (tr "workspace.libraries.add")
|
||||
:on-click #(link-library (:id file))}]])]
|
||||
[:div.section-list-empty
|
||||
i/library
|
||||
(if (str/empty? (:search-term @state))
|
||||
(tr "workspace.libraries.no-shared-libraries-available")
|
||||
(tr "workspace.libraries.no-matches-for" (:search-term @state)))])]]))
|
||||
|
||||
|
||||
(mf/defc updates-tab
|
||||
[]
|
||||
[:div])
|
||||
|
||||
|
||||
(mf/defc libraries-dialog
|
||||
[{:keys [] :as ctx}]
|
||||
(let [state (mf/use-state {:current-tab :libraries})
|
||||
|
||||
current-tab (:current-tab @state)
|
||||
|
||||
file (mf/deref refs/workspace-file)
|
||||
libraries (mf/deref refs/workspace-libraries)
|
||||
shared-files (mf/deref refs/workspace-shared-files)
|
||||
|
||||
change-tab (fn [tab]
|
||||
(swap! state assoc :current-tab tab))
|
||||
|
||||
close (fn [event]
|
||||
(dom/prevent-default event)
|
||||
(modal/hide!))]
|
||||
|
||||
(mf/use-effect
|
||||
#(st/emit! (dw/fetch-shared-files)))
|
||||
|
||||
[:div.modal-overlay
|
||||
[:div.modal.libraries-dialog
|
||||
[:a.close {:on-click close} i/close]
|
||||
[:div.modal-content
|
||||
[:div.libraries-header
|
||||
[:div.header-item
|
||||
{:class (classnames :active (= current-tab :libraries))
|
||||
:on-click #(change-tab :libraries)}
|
||||
(tr "workspace.libraries.libraries")]
|
||||
[:div.header-item
|
||||
{:class (classnames :active (= current-tab :updates))
|
||||
:on-click #(change-tab :updates)}
|
||||
(tr "workspace.libraries.updates")]]
|
||||
[:div.libraries-content
|
||||
(case current-tab
|
||||
:libraries
|
||||
[:& libraries-tab {:file file
|
||||
:libraries libraries
|
||||
:shared-files shared-files}]
|
||||
:updates
|
||||
[:& updates-tab {}])]]]]))
|
||||
|
|
@ -29,20 +29,14 @@
|
|||
[uxbox.util.timers :as timers]
|
||||
[uxbox.common.uuid :as uuid]
|
||||
[uxbox.util.i18n :as i18n :refer [tr]]
|
||||
[uxbox.util.data :refer [classnames]]
|
||||
[uxbox.util.data :refer [classnames matches-search]]
|
||||
[uxbox.util.router :as rt]
|
||||
[uxbox.main.ui.modal :as modal]
|
||||
[uxbox.main.ui.colorpicker :refer [colorpicker most-used-colors]]
|
||||
[uxbox.main.ui.components.tab-container :refer [tab-container tab-element]]
|
||||
[uxbox.main.ui.components.file-uploader :refer [file-uploader]]
|
||||
[uxbox.main.ui.components.context-menu :refer [context-menu]]))
|
||||
|
||||
(defn matches-search
|
||||
[name search-term]
|
||||
(if (str/empty? search-term)
|
||||
true
|
||||
(let [st (str/trim (str/lower search-term))
|
||||
nm (str/trim (str/lower name))]
|
||||
(str/includes? nm st))))
|
||||
[uxbox.main.ui.components.context-menu :refer [context-menu]]
|
||||
[uxbox.main.ui.workspace.libraries :refer [libraries-dialog]]))
|
||||
|
||||
(mf/defc modal-edit-color
|
||||
[{:keys [color-value on-accept on-cancel] :as ctx}]
|
||||
|
@ -72,7 +66,7 @@
|
|||
[:a.close {:href "#" :on-click cancel} i/close]]])))
|
||||
|
||||
(mf/defc graphics-box
|
||||
[{:keys [file-id media-objects] :as props}]
|
||||
[{:keys [file-id local-library? media-objects] :as props}]
|
||||
(let [state (mf/use-state {:menu-open false
|
||||
:top nil
|
||||
:left nil
|
||||
|
@ -84,7 +78,7 @@
|
|||
#(dom/click (mf/ref-val file-input))
|
||||
|
||||
delete-graphic
|
||||
#(st/emit! (dw/delete-media-object (:object-id @state)))
|
||||
#(st/emit! (dw/delete-media-object file-id (:object-id @state)))
|
||||
|
||||
on-files-selected
|
||||
(fn [js-files]
|
||||
|
@ -93,14 +87,15 @@
|
|||
on-context-menu
|
||||
(fn [object-id]
|
||||
(fn [event]
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc :menu-open true
|
||||
:top top
|
||||
:left left
|
||||
:object-id object-id))))
|
||||
(when local-library?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc :menu-open true
|
||||
:top top
|
||||
:left left
|
||||
:object-id object-id)))))
|
||||
|
||||
on-drag-start
|
||||
(fn [uri]
|
||||
|
@ -112,14 +107,15 @@
|
|||
[:div.group-title
|
||||
(tr "workspace.assets.graphics")
|
||||
[:span (str "\u00A0(") (count media-objects) ")"] ;; Unicode 00A0 is non-breaking space
|
||||
[:div.group-button {:on-click add-graphic}
|
||||
i/plus
|
||||
[:& file-uploader {:accept cm/str-media-types
|
||||
:multi true
|
||||
:input-ref file-input
|
||||
:on-selected on-files-selected}]]]
|
||||
(when local-library?
|
||||
[:div.group-button {:on-click add-graphic}
|
||||
i/plus
|
||||
[:& file-uploader {:accept cm/str-media-types
|
||||
:multi true
|
||||
:input-ref file-input
|
||||
:on-selected on-files-selected}]])]
|
||||
[:div.group-grid
|
||||
(for [object (sort-by :name media-objects)]
|
||||
(for [object media-objects]
|
||||
[:div.grid-cell {:key (:id object)
|
||||
:draggable true
|
||||
:on-context-menu (on-context-menu (:id object))
|
||||
|
@ -127,17 +123,18 @@
|
|||
[:img {:src (:thumb-uri object)
|
||||
:draggable false}] ;; Also need to add css pointer-events: none
|
||||
[:div.cell-name (:name object)]])
|
||||
[:& context-menu
|
||||
{:selectable false
|
||||
:show (:menu-open @state)
|
||||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [[(tr "workspace.assets.delete") delete-graphic]]}]]]))
|
||||
(when local-library?
|
||||
[:& context-menu
|
||||
{:selectable false
|
||||
:show (:menu-open @state)
|
||||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [[(tr "workspace.assets.delete") delete-graphic]]}])]]))
|
||||
|
||||
|
||||
(mf/defc color-item
|
||||
[{:keys [color file-id] :as props}]
|
||||
[{:keys [color file-id local-library?] :as props}]
|
||||
(let [workspace-local @refs/workspace-local
|
||||
color-for-rename (:color-for-rename workspace-local)
|
||||
|
||||
|
@ -189,14 +186,15 @@
|
|||
|
||||
on-context-menu
|
||||
(fn [event]
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc
|
||||
:menu-open true
|
||||
:top top
|
||||
:left left)))]
|
||||
(when local-library?
|
||||
(let [pos (dom/get-client-position event)
|
||||
top (:y pos)
|
||||
left (- (:x pos) 20)]
|
||||
(dom/prevent-default event)
|
||||
(swap! state assoc
|
||||
:menu-open true
|
||||
:top top
|
||||
:left left))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps (:editing @state))
|
||||
|
@ -220,18 +218,19 @@
|
|||
(:name color)
|
||||
(when-not (= (:name color) (:content color))
|
||||
[:span (:content color)])])
|
||||
[:& context-menu
|
||||
{:selectable false
|
||||
:show (:menu-open @state)
|
||||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [[(tr "workspace.assets.rename") rename-color-clicked]
|
||||
[(tr "workspace.assets.edit") edit-color-clicked]
|
||||
[(tr "workspace.assets.delete") delete-color]]}]]))
|
||||
(when local-library?
|
||||
[:& context-menu
|
||||
{:selectable false
|
||||
:show (:menu-open @state)
|
||||
:on-close #(swap! state assoc :menu-open false)
|
||||
:top (:top @state)
|
||||
:left (:left @state)
|
||||
:options [[(tr "workspace.assets.rename") rename-color-clicked]
|
||||
[(tr "workspace.assets.edit") edit-color-clicked]
|
||||
[(tr "workspace.assets.delete") delete-color]]}])]))
|
||||
|
||||
(mf/defc colors-box
|
||||
[{:keys [file-id colors] :as props}]
|
||||
[{:keys [file-id local-library? colors] :as props}]
|
||||
(let [add-color
|
||||
(fn [value opacity]
|
||||
(st/emit! (dcol/create-color file-id value)))
|
||||
|
@ -246,15 +245,18 @@
|
|||
[:div.group-title
|
||||
(tr "workspace.assets.colors")
|
||||
[:span (str "\u00A0(") (count colors) ")"] ;; Unicode 00A0 is non-breaking space
|
||||
[:div.group-button {:on-click add-color-clicked} i/plus]]
|
||||
(when local-library?
|
||||
[:div.group-button {:on-click add-color-clicked} i/plus])]
|
||||
[:div.group-list
|
||||
(for [color (sort-by :name colors)]
|
||||
(for [color colors]
|
||||
[:& color-item {:key (:id color)
|
||||
:color color
|
||||
:file-id file-id}])]]))
|
||||
:file-id file-id
|
||||
:local-library? local-library?}])]]))
|
||||
|
||||
(mf/defc file-library-toolbox
|
||||
[{:keys [file-id
|
||||
[{:keys [library
|
||||
local-library?
|
||||
shared?
|
||||
media-objects
|
||||
colors
|
||||
|
@ -262,26 +264,41 @@
|
|||
search-term
|
||||
box-filter] :as props}]
|
||||
(let [open? (mf/use-state initial-open?)
|
||||
toggle-open #(swap! open? not)]
|
||||
toggle-open #(swap! open? not)
|
||||
router (mf/deref refs/router)
|
||||
library-url (rt/resolve router :workspace
|
||||
{:project-id (:project-id library)
|
||||
:file-id (:id library)}
|
||||
{:page-id (first (:pages library))})]
|
||||
[:div.tool-window
|
||||
[:div.tool-window-bar
|
||||
[:div.collapse-library
|
||||
{:class (classnames :open @open?)
|
||||
:on-click toggle-open}
|
||||
i/arrow-slide]
|
||||
[:span (tr "workspace.assets.file-library")]
|
||||
(when shared?
|
||||
[:span.tool-badge (tr "workspace.assets.shared")])]
|
||||
(if local-library?
|
||||
[:*
|
||||
[:span (tr "workspace.assets.file-library")]
|
||||
(when shared?
|
||||
[:span.tool-badge (tr "workspace.assets.shared")])]
|
||||
[:*
|
||||
[:span (:name library)]
|
||||
[:span.tool-link
|
||||
[:a {:href (str "#" library-url) :target "_blank"} i/chain]]])]
|
||||
(when @open?
|
||||
(let [show-graphics (and (or (= box-filter :all) (= box-filter :graphics))
|
||||
(or (> (count media-objects) 0) (str/empty? search-term)))
|
||||
show-colors (and (or (= box-filter :all) (= box-filter :colors))
|
||||
(or (> (count colors) 0) (str/empty? search-term)))]
|
||||
(or (> (count media-objects) 0) (str/empty? search-term)))
|
||||
show-colors (and (or (= box-filter :all) (= box-filter :colors))
|
||||
(or (> (count colors) 0) (str/empty? search-term)))]
|
||||
[:div.tool-window-content
|
||||
(when show-graphics
|
||||
[:& graphics-box {:file-id file-id :media-objects media-objects}])
|
||||
[:& graphics-box {:file-id (:id library)
|
||||
:local-library? local-library?
|
||||
:media-objects media-objects}])
|
||||
(when show-colors
|
||||
[:& colors-box {:file-id file-id :colors colors}])
|
||||
[:& colors-box {:file-id (:id library)
|
||||
:local-library? local-library?
|
||||
:colors colors}])
|
||||
(when (and (not show-graphics) (not show-colors))
|
||||
[:div.asset-group
|
||||
[:div.group-title (tr "workspace.assets.not-found")]])]))]))
|
||||
|
@ -290,19 +307,27 @@
|
|||
[]
|
||||
(let [team-id (-> refs/workspace-project mf/deref :team-id)
|
||||
file (mf/deref refs/workspace-file)
|
||||
file-id (:id file)
|
||||
file-media (mf/deref refs/workspace-media-library)
|
||||
file-colors (mf/deref refs/workspace-colors-library)
|
||||
libraries (mf/deref refs/workspace-libraries)
|
||||
sorted-libraries (->> (vals libraries)
|
||||
(sort-by #(str/lower (:name %))))
|
||||
|
||||
state (mf/use-state {:search-term ""
|
||||
:box-filter :all})
|
||||
|
||||
filtered-media-objects (filter #(matches-search (:name %) (:search-term @state))
|
||||
(vals file-media))
|
||||
filtered-media-objects (fn [library-id]
|
||||
(as-> libraries $$
|
||||
(assoc $$ (:id file) file)
|
||||
(get-in $$ [library-id :media-objects])
|
||||
(filter #(matches-search (:name %) (:search-term @state)) $$)
|
||||
(sort-by #(str/lower (:name %)) $$)))
|
||||
|
||||
filtered-colors (filter #(or (matches-search (:name %) (:search-term @state))
|
||||
(matches-search (:content %) (:search-term @state)))
|
||||
(vals file-colors))
|
||||
filtered-colors (fn [library-id]
|
||||
(as-> libraries $$
|
||||
(assoc $$ (:id file) file)
|
||||
(get-in $$ [library-id :colors])
|
||||
(filter #(or (matches-search (:name %) (:search-term @state))
|
||||
(matches-search (:content %) (:search-term @state))) $$)
|
||||
(sort-by #(str/lower (:name %)) $$)))
|
||||
|
||||
on-search-term-change (fn [event]
|
||||
(let [value (-> (dom/get-target event)
|
||||
|
@ -318,17 +343,15 @@
|
|||
(d/read-string))]
|
||||
(swap! state assoc :box-filter value)))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps file-id)
|
||||
#(when file-id
|
||||
(st/emit! (dw/fetch-media-library file-id))
|
||||
(st/emit! (dw/fetch-colors-library file-id))))
|
||||
|
||||
[:div.assets-bar
|
||||
|
||||
[:div.tool-window
|
||||
[:div.tool-window-content
|
||||
[:div.assets-bar-title (tr "workspace.assets.assets")]
|
||||
[:div.assets-bar-title
|
||||
(tr "workspace.assets.assets")
|
||||
[:div.libraries-button {:on-click #(modal/show! libraries-dialog {})}
|
||||
i/libraries
|
||||
(tr "workspace.assets.libraries")]]
|
||||
|
||||
[:div.search-block
|
||||
[:input.search-input
|
||||
|
@ -347,14 +370,25 @@
|
|||
:on-change on-box-filter-change}
|
||||
[:option {:value ":all"} (tr "workspace.assets.box-filter-all")]
|
||||
[:option {:value ":graphics"} (tr "workspace.assets.box-filter-graphics")]
|
||||
[:option {:value ":colors"} (tr "workspace.assets.box-filter-colors")]]
|
||||
]]
|
||||
[:option {:value ":colors"} (tr "workspace.assets.box-filter-colors")]]]]
|
||||
|
||||
[:& file-library-toolbox {:file-id file-id
|
||||
[:& file-library-toolbox {:key (:id file)
|
||||
:library file
|
||||
:local-library? true
|
||||
:shared? (:is-shared file)
|
||||
:media-objects filtered-media-objects
|
||||
:colors filtered-colors
|
||||
:media-objects (filtered-media-objects (:id file))
|
||||
:colors (filtered-colors (:id file))
|
||||
:initial-open? true
|
||||
:search-term (:search-term @state)
|
||||
:box-filter (:box-filter @state)}]]))
|
||||
:box-filter (:box-filter @state)}]
|
||||
(for [library sorted-libraries]
|
||||
[:& file-library-toolbox {:key (:id library)
|
||||
:library library
|
||||
:local-library? false
|
||||
:shared? (:is-shared library)
|
||||
:media-objects (filtered-media-objects (:id library))
|
||||
:colors (filtered-colors (:id library))
|
||||
:initial-open? false
|
||||
:search-term (:search-term @state)
|
||||
:box-filter (:box-filter @state)}])]))
|
||||
|
||||
|
|
|
@ -180,6 +180,13 @@
|
|||
"className"
|
||||
(str/camel (name key))))))
|
||||
|
||||
(defn matches-search
|
||||
[name search-term]
|
||||
(if (str/empty? search-term)
|
||||
true
|
||||
(let [st (str/trim (str/lower search-term))
|
||||
nm (str/trim (str/lower name))]
|
||||
(str/includes? nm st))))
|
||||
|
||||
;; (defn coalesce
|
||||
;; [^number v ^number n]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue