2020-09-28 12:28:29 +02:00

251 lines
8.3 KiB
Clojure

;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020 UXBOX Labs SL
(ns app.main.ui.dashboard.grid
(:require
[app.common.uuid :as uuid]
[app.common.math :as mth]
[app.config :as cfg]
[app.main.data.dashboard :as dd]
[app.main.fonts :as fonts]
[app.main.store :as st]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
[app.main.ui.modal :as modal]
[app.main.worker :as wrk]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [t tr]]
[app.util.router :as rt]
[app.util.time :as dt]
[app.util.timers :as ts]
[beicon.core :as rx]
[cuerdas.core :as str]
[lambdaisland.uri :as uri]
[rumext.alpha :as mf]))
;; --- Grid Item Thumbnail
(mf/defc grid-item-thumbnail
{::mf/wrap [mf/memo]}
[{:keys [file] :as props}]
(let [container (mf/use-ref)]
(mf/use-effect
(mf/deps (:id file))
(fn []
(->> (wrk/ask! {:cmd :thumbnails/generate
:file-id (:id file)
:page-id (get-in file [:data :pages 0])})
(rx/subs (fn [{:keys [svg fonts]}]
(run! fonts/ensure-loaded! fonts)
(when-let [node (mf/ref-val container)]
(set! (.-innerHTML ^js node) svg)))))))
[:div.grid-item-th {:style {:background-color (get-in file [:data :options :background])}
:ref container}]))
;; --- Grid Item
(mf/defc grid-item-metadata
[{:keys [modified-at]}]
(let [locale (mf/deref i18n/locale)
time (dt/timeago modified-at {:locale locale})]
(str (t locale "ds.updated-at" time))))
(mf/defc grid-item
{:wrap [mf/memo]}
[{:keys [id file] :as props}]
(let [local (mf/use-state {:menu-open false :edition false})
locale (mf/deref i18n/locale)
delete (mf/use-callback (mf/deps id) #(st/emit! (dd/delete-file file)))
add-shared (mf/use-callback (mf/deps id) #(st/emit! (dd/set-file-shared id true)))
del-shared (mf/use-callback (mf/deps id) #(st/emit! (dd/set-file-shared id false)))
on-close (mf/use-callback #(swap! local assoc :menu-open false))
on-delete
(mf/use-callback
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(modal/show! :confirm-dialog {:on-accept delete})))
on-navigate
(mf/use-callback
(mf/deps id)
(fn []
(let [pparams {:project-id (:project-id file)
:file-id (:id file)}
qparams {:page-id (first (get-in file [:data :pages]))}]
(st/emit! (rt/nav :workspace pparams qparams)))))
on-add-shared
(mf/use-callback
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(modal/show! :confirm-dialog
{:message (t locale "dashboard.grid.add-shared-message" (:name file))
:hint (t locale "dashboard.grid.add-shared-hint")
:accept-text (t locale "dashboard.grid.add-shared-accept")
:not-danger? true
:on-accept add-shared})))
on-edit
(mf/use-callback
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(swap! local assoc :edition true)))
on-del-shared
(mf/use-callback
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(modal/show! :confirm-dialog
{:message (t locale "dashboard.grid.remove-shared-message" (:name file))
:hint (t locale "dashboard.grid.remove-shared-hint")
:accept-text (t locale "dashboard.grid.remove-shared-accept")
:not-danger? false
:on-accept del-shared})))
on-menu-click
(mf/use-callback
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(swap! local assoc :menu-open true)))
on-blur
(mf/use-callback
(mf/deps id)
(fn [event]
(let [name (-> event dom/get-target dom/get-value)
file (assoc file :name name)]
(st/emit! (dd/rename-file file))
(swap! local assoc :edition false))))
on-key-down
(mf/use-callback
#(cond
(kbd/enter? %) (on-blur %)
(kbd/esc? %) (swap! local assoc :edition false)))
]
[:div.grid-item.project-th {:on-click on-navigate}
[:div.overlay]
[:& grid-item-thumbnail {:file file}]
(when (:is-shared file)
[:div.item-badge
i/library])
[:div.item-info
(if (:edition @local)
[:input.element-name {:type "text"
:auto-focus true
:on-key-down on-key-down
:on-blur on-blur
:default-value (:name file)}]
[:h3 (:name file)])
[:& grid-item-metadata {:modified-at (:modified-at file)}]]
[:div.project-th-actions {:class (dom/classnames
:force-display (:menu-open @local))}
[:div.project-th-icon.menu
{:on-click on-menu-click}
i/actions]
[:& context-menu {:on-close on-close
:show (:menu-open @local)
:options [[(t locale "dashboard.grid.rename") on-edit]
[(t locale "dashboard.grid.delete") on-delete]
(if (:is-shared file)
[(t locale "dashboard.grid.remove-shared") on-del-shared]
[(t locale "dashboard.grid.add-shared") on-add-shared])]}]]]))
(mf/defc empty-placeholder
[]
(let [locale (mf/deref i18n/locale)]
[:div.grid-empty-placeholder
[:div.icon i/file-html]
[:div.text (t locale "dashboard.grid.empty-files")]]))
(mf/defc grid
[{:keys [id opts files hide-new?] :as props}]
(let [locale (mf/deref i18n/locale)
click #(st/emit! (dd/create-file id))]
[:section.dashboard-grid
(if (pos? (count files))
[:div.dashboard-grid-row
(when (not hide-new?)
[:div.grid-item.add-file {:on-click click}
[:span (t locale "dashboard.new-file")]])
(for [item files]
[:& grid-item
{:id (:id item)
:file item
:key (:id item)}])]
[:& empty-placeholder])]))
(mf/defc line-grid-row
[{:keys [locale files] :as props}]
(let [rowref (mf/use-ref)
width (mf/use-state 900)
limit (mf/use-state 1)
itemsize 290]
(mf/use-layout-effect
(mf/deps width)
(fn []
(let [node (mf/ref-val rowref)
obs (new js/ResizeObserver
(fn [entries x]
(let [data (first entries)
rect (.-contentRect ^js data)]
(reset! width (.-width ^js rect)))))
nitems (/ @width itemsize)
num (mth/floor nitems)]
(.observe ^js obs node)
(cond
(< (* itemsize (count files)) @width)
(reset! limit num)
(< nitems (+ num 0.51))
(reset! limit (dec num))
:else
(reset! limit num))
(fn []
(.disconnect ^js obs)))))
[:div.grid-row.no-wrap {:ref rowref}
(for [item (take @limit files)]
[:& grid-item
{:id (:id item)
:file item
:key (:id item)}])
(when (> (count files) @limit)
[:div.grid-item.placeholder
[:div.placeholder-icon i/arrow-down]
[:div.placeholder-label "Show all files"]])]))
(mf/defc line-grid
[{:keys [project-id opts files] :as props}]
(let [locale (mf/deref i18n/locale)
click #(st/emit! (dd/create-file project-id))]
[:section.dashboard-grid
(if (pos? (count files))
[:& line-grid-row {:files files
:locale locale}]
[:& empty-placeholder])]))