0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-25 22:21:44 -05:00

🎉 Picker shortcut

This commit is contained in:
alonso.torres 2020-09-10 14:06:46 +02:00 committed by Alonso Torres
parent f8b3baef3f
commit 726cdb9a27
23 changed files with 236 additions and 110 deletions

View file

@ -18,7 +18,7 @@
funcool/okulary {:mvn/version "2020.04.14-0"}
funcool/potok {:mvn/version "2020.08.10-2"}
funcool/promesa {:mvn/version "5.1.0"}
funcool/rumext {:mvn/version "2020.05.22-1"}
funcool/rumext {:mvn/version "2020.08.21-0"}
lambdaisland/uri {:mvn/version "1.3.45"
:exclusions [org.clojure/data.json]}

View file

@ -26,7 +26,13 @@
[app.util.router :as rt]
[app.util.object :as obj]
[app.util.storage :refer [storage]]
[app.util.timers :as ts]))
[app.util.timers :as ts]
;; MODALS
[app.main.ui.settings.delete-account]
[app.main.ui.settings.change-email]
[app.main.ui.confirm]
[app.main.ui.workspace.colorpicker]))
(declare reinit)

View file

@ -10,6 +10,7 @@
[beicon.core :as rx]
[clojure.set :as set]
[potok.core :as ptk]
[app.main.streams :as ms]
[app.common.data :as d]
[app.common.spec :as us]
[app.main.repo :as rp]
@ -18,8 +19,9 @@
[app.util.i18n :refer [tr]]
[app.util.router :as rt]
[app.util.time :as dt]
[app.common.uuid :as uuid]))
[app.common.uuid :as uuid]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.texts :as dwt]))
(declare create-color-result)
@ -142,6 +144,8 @@
ptk/UpdateEvent
(update [_ state]
(-> state
(update :workspace-local dissoc :picked-color-select)
(update :workspace-local dissoc :picked-shift?)
(assoc-in [:workspace-local :picking-color?] false)))))
(defn pick-color [rgba]
@ -151,9 +155,61 @@
(-> state
(assoc-in [:workspace-local :picked-color] rgba)))))
(defn pick-color-select [value]
(ptk/reify ::pick-color
(defn pick-color-select [value shift?]
(ptk/reify ::pick-color-select
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :picked-color-select] value)))))
(assoc-in [:workspace-local :picked-color-select] value)
(assoc-in [:workspace-local :picked-shift?] shift?)))))
(defn change-fill-selected [color]
(ptk/reify ::change-fill-selected
ptk/WatchEvent
(watch [_ state s]
(let [ids (get-in state [:workspace-local :selected])
objects (get-in state [:workspace-data :pages-index (:current-page-id state) :objects])
is-text? #(= :text (:type (get objects %)))
text-ids (filter is-text? ids)
shape-ids (filter (comp not is-text?) ids)
update-fn (fn [shape] (assoc shape :fill-color color))
editor (get-in state [:workspace-local :editor])
converted-attrs {:fill color}]
(rx/from (conj
(map #(dwt/update-text-attrs {:id % :editor editor :attrs converted-attrs}) text-ids)
(dwc/update-shapes shape-ids update-fn)))))))
(defn change-stroke-selected [color]
(ptk/reify ::change-stroke-selected
ptk/WatchEvent
(watch [_ state s]
(let [ids (get-in state [:workspace-local :selected])
update-fn (fn [s]
(cond-> s
true
(assoc :stroke-color color)
(= (:stroke-style s) :none)
(assoc :stroke-style "solid"
:stroke-width 1
:stroke-opacity 1)))]
(rx/of (dwc/update-shapes ids update-fn))))))
(defn picker-for-selected-shape []
(let [handle-change-color (fn [color _ shift?]
(st/emit!
(if shift?
(change-stroke-selected color)
(change-fill-selected color)
)
(fn [state] (update state :workspace-local dissoc :modal))))]
(ptk/reify ::start-picker
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :picking-color?] true)
(assoc-in [:workspace-local :modal] {:id (random-uuid)
:type :colorpicker
:props {:on-change handle-change-color}
:allow-click-outside true}))))))

View file

@ -27,6 +27,7 @@
[app.main.data.workspace.selection :as dws]
[app.main.data.workspace.texts :as dwtxt]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.colors :as dwl]
[app.main.repo :as rp]
[app.main.store :as st]
[app.main.streams :as ms]
@ -1546,5 +1547,7 @@
"up" #(st/emit! (dwt/move-selected :up false))
"down" #(st/emit! (dwt/move-selected :down false))
"right" #(st/emit! (dwt/move-selected :right false))
"left" #(st/emit! (dwt/move-selected :left false))})
"left" #(st/emit! (dwt/move-selected :left false))
"i" #(st/emit! (dwl/picker-for-selected-shape ))})

View file

@ -89,6 +89,9 @@
(def picked-color-select
(l/derived :picked-color-select workspace-local))
(def picked-shift?
(l/derived :picked-shift? workspace-local))
(def workspace-layout
(l/derived :workspace-layout st/state))

View file

@ -15,6 +15,8 @@
[app.util.dom :as dom]))
(mf/defc confirm-dialog
{::mf/register modal/components
::mf/register-as :confirm-dialog}
[{:keys [message on-accept on-cancel hint cancel-text accept-text not-danger?] :as ctx}]
(let [message (or message (tr "ds.confirm-title"))
cancel-text (or cancel-text (tr "ds.confirm-cancel"))

View file

@ -15,7 +15,6 @@
[app.main.fonts :as fonts]
[app.main.store :as st]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.confirm :refer [confirm-dialog]]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
[app.main.ui.modal :as modal]
@ -73,7 +72,7 @@
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(modal/show! confirm-dialog {:on-accept delete})))
(modal/show! :confirm-dialog {:on-accept delete})))
on-navigate
(mf/use-callback
@ -89,7 +88,7 @@
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(modal/show! confirm-dialog
(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")
@ -108,7 +107,7 @@
(mf/deps id)
(fn [event]
(dom/stop-propagation event)
(modal/show! confirm-dialog
(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")

View file

@ -19,7 +19,6 @@
[app.main.store :as st]
[app.main.ui.modal :as modal]
[app.main.ui.keyboard :as kbd]
[app.main.ui.confirm :refer [confirm-dialog]]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.dashboard.grid :refer [grid]]))

View file

@ -19,7 +19,6 @@
[app.main.store :as st]
[app.main.ui.modal :as modal]
[app.main.ui.keyboard :as kbd]
[app.main.ui.confirm :refer [confirm-dialog]]
[app.main.ui.components.context-menu :refer [context-menu]]
[app.main.ui.dashboard.grid :refer [grid]]))
@ -49,7 +48,7 @@
delete-fn #(do
(st/emit! (dsh/delete-project project-id))
(st/emit! (rt/nav :dashboard-team {:team-id team-id})))
on-delete #(modal/show! confirm-dialog {:on-accept delete-fn})]
on-delete #(modal/show! :confirm-dialog {:on-accept delete-fn})]
[:header.main-bar
(if (:is-default project)
[:h1.dashboard-title (t locale "dashboard.header.draft")]
@ -83,4 +82,4 @@
[:*
[:& project-header {:team-id team-id :project-id project-id}]
[:section.projects-page
[:& grid { :id project-id :files files :hide-new? true}]]]))
[:& grid { :id project-id :files files :hide-new? true}]]]))

View file

@ -16,11 +16,9 @@
[app.main.data.dashboard :as dsh]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.confirm :refer [confirm-dialog]]
[app.main.ui.dashboard.grid :refer [grid]]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
[app.main.ui.modal :as modal]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [t tr]]
[app.util.router :as rt]

View file

@ -18,11 +18,9 @@
[app.main.data.dashboard :as dsh]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.confirm :refer [confirm-dialog]]
[app.main.ui.dashboard.common :as common]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
[app.main.ui.modal :as modal]
[app.util.dom :as dom]
[app.util.i18n :as i18n :refer [t tr]]
[app.util.router :as rt]

View file

@ -1,69 +1,126 @@
;; 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.modal
(:require
[cuerdas.core :as str]
[okulary.core :as l]
[goog.events :as events]
[rumext.alpha :as mf]
[app.main.store :as st]
[app.main.ui.keyboard :as k]
[app.util.data :refer [classnames]]
[app.util.dom :as dom])
[app.util.dom :as dom]
[app.main.refs :as refs]
[potok.core :as ptk])
(:import goog.events.EventType))
(defonce state (atom nil))
(defonce can-click-outside (atom false))
(defonce components (atom {}))
(defn show-modal [id type props]
(ptk/reify ::show-modal
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :modal] {:id id
:type type
:props props
:allow-click-outside false})))))
(defn hide-modal []
(ptk/reify ::hide-modal
ptk/UpdateEvent
(update [_ state]
(-> state
(update :workspace-local dissoc :modal)))))
(defn update-modal [options]
(ptk/reify ::hide-modal
ptk/UpdateEvent
(update [_ state]
(-> state
(update-in [:workspace-local :modal] merge options)))))
(defn show!
[component props]
(reset! state {:component component :props props}))
([type props]
(let [id (random-uuid)]
(st/emit! (show-modal id type props)))))
(defn allow-click-outside! []
(st/emit! (update-modal {:allow-click-outside true})))
(defn disallow-click-outside! []
(st/emit! (update-modal {:allow-click-outside false})))
(defn hide!
[]
(reset! state nil))
(st/emit! (hide-modal)))
(defn- on-esc-clicked
[event]
(when (k/esc? event)
(reset! state nil)
(hide!)
(dom/stop-propagation event)))
(defn- on-click
[event wrapper-ref]
(defn- on-pop-state
[event]
(dom/prevent-default event)
(dom/stop-propagation event)
(hide!)
(.forward js/history))
(defn- on-parent-clicked
[event parent-ref]
(let [parent (mf/ref-val parent-ref)
current (dom/get-target event)]
(when (and (dom/equals? (.-firstElementChild ^js parent) current)
(= (.-className ^js current) "modal-overlay"))
(dom/stop-propagation event)
(dom/prevent-default event)
(hide!))))
(defn- on-click-outside
[event wrapper-ref allow-click-outside]
(let [wrapper (mf/ref-val wrapper-ref)
current (dom/get-target event)]
(when (and (not @can-click-outside) (not (.contains wrapper current)))
(when (and wrapper (not allow-click-outside) (not (.contains wrapper current)))
(dom/stop-propagation event)
(dom/prevent-default event)
(reset! state nil))))
(hide!))))
(mf/defc modal-wrapper
[{:keys [component props]}]
{::mf/wrap-props false
::mf/wrap [mf/memo]}
[props]
(let [data (unchecked-get props "data")
wrapper-ref (mf/use-ref nil)
handle-click-outside
(fn [event]
(on-click-outside event wrapper-ref (:allow-click-outside data)))]
(let [wrapper-ref (mf/use-ref nil)]
(mf/use-effect
(mf/use-layout-effect
(fn []
(let [key (events/listen js/document EventType.KEYDOWN on-esc-clicked)]
#(events/unlistenByKey key))))
(let [keys [(events/listen js/document EventType.KEYDOWN on-esc-clicked)
(events/listen js/window EventType.POPSTATE on-pop-state)
(events/listen js/document EventType.CLICK handle-click-outside)]]
#(for [key keys]
(events/unlistenByKey key)))))
[:div.modal-wrapper {:ref wrapper-ref}
(mf/element
(get @components (:type data))
(:props data))]))
(mf/use-effect
(fn []
(let [key (events/listen js/document EventType.CLICK #(on-click % wrapper-ref))]
#(events/unlistenByKey key))))
[:div.modal-wrapper
{:ref wrapper-ref}
[:& component props]]))
(def modal-ref
(l/derived :modal refs/workspace-local))
(mf/defc modal
[]
(when-let [{:keys [component props]} (mf/deref state)]
[:& modal-wrapper {:component component
:props props
:key (random-uuid)}]))
(defn allow-click-outside! []
(reset! can-click-outside true))
(defn disallow-click-outside! []
(reset! can-click-outside false))
(let [modal (mf/deref modal-ref)]
(when modal [:& modal-wrapper {:data modal
:key (:id modal)}])))

View file

@ -98,6 +98,8 @@
(t locale "settings.close-modal-label")]])
(mf/defc change-email-modal
{::mf/register modal/components
::mf/register-as :change-email}
[props]
(let [locale (mf/deref i18n/locale)
profile (mf/deref refs/profile)]

View file

@ -20,24 +20,27 @@
[app.util.i18n :as i18n :refer [tr t]]))
(mf/defc delete-account-modal
{::mf/register modal/components
::mf/register-as :delete-account}
[props]
(let [locale (mf/deref i18n/locale)]
[:section.generic-modal.change-email-modal
[:span.close {:on-click #(modal/hide!)} i/close]
[:div.modal-overlay
[:section.generic-modal.change-email-modal
[:span.close {:on-click #(modal/hide!)} i/close]
[:section.modal-content.generic-form
[:h2 (t locale "settings.delete-account-title")]
[:section.modal-content.generic-form
[:h2 (t locale "settings.delete-account-title")]
[:& msgs/inline-banner
{:type :warning
:content (t locale "settings.delete-account-info")}]
[:& msgs/inline-banner
{:type :warning
:content (t locale "settings.delete-account-info")}]
[:div.button-row
[:button.btn-warning.btn-large
{:on-click #(do
(modal/hide!)
(st/emit! da/request-account-deletion))}
(t locale "settings.yes-delete-my-account")]
[:button.btn-secondary.btn-large
{:on-click #(modal/hide!)}
(t locale "settings.cancel-and-keep-my-account")]]]]))
[:div.button-row
[:button.btn-warning.btn-large
{:on-click #(do
(modal/hide!)
(st/emit! da/request-account-deletion))}
(t locale "settings.yes-delete-my-account")]
[:button.btn-secondary.btn-large
{:on-click #(modal/hide!)}
(t locale "settings.cancel-and-keep-my-account")]]]]]))

View file

@ -21,8 +21,6 @@
[app.main.ui.icons :as i]
[app.main.ui.messages :as msgs]
[app.main.ui.modal :as modal]
[app.main.ui.settings.change-email :refer [change-email-modal]]
[app.main.ui.settings.delete-account :refer [delete-account-modal]]
[app.util.dom :as dom]
[app.util.forms :as fm]
[app.util.i18n :as i18n :refer [tr t]]))
@ -71,7 +69,7 @@
(cond
(nil? (:pending-email prof))
[:div.change-email
[:a {:on-click #(modal/show! change-email-modal {})}
[:a {:on-click #(modal/show! :change-email {})}
(t locale "settings.change-email-label")]]
(not= (:pending-email prof) (:email prof))
@ -92,7 +90,7 @@
[:div.links
[:div.link-item
[:a {:on-click #(modal/show! delete-account-modal {})}
[:a {:on-click #(modal/show! :delete-account {})}
(t locale "settings.remove-account-label")]]]]))
;; --- Profile Photo Form

View file

@ -19,6 +19,7 @@
[app.common.uuid :refer [uuid]]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.colors :as dwc]
#_[app.main.ui.modal :as modal]
[app.main.ui.modal :as modal]
[okulary.core :as l]
[app.main.refs :as refs]
@ -105,6 +106,7 @@
picking-color? (mf/deref refs/picking-color?)
picked-color (mf/deref refs/picked-color)
picked-color-select (mf/deref refs/picked-color-select)
picked-shift? (mf/deref refs/picked-shift?)
locale (mf/deref i18n/locale)
@ -158,8 +160,10 @@
;; When closing the modal we update the recent-color list
(mf/use-effect
(fn [] #(st/emit! (dwc/stop-picker)
(dwl/add-recent-color @value-ref))))
(fn [] (fn []
(st/emit! (dwc/stop-picker))
(when @value-ref
(st/emit! (dwl/add-recent-color @value-ref))))))
(mf/use-effect
(mf/deps picking-color? picked-color)
@ -172,12 +176,12 @@
:h h :s s :v v
:hex hex)
(when picked-color-select
(on-change hex (:alpha @current-color)))))))
(on-change hex (:alpha @current-color) picked-shift?))))))
(mf/use-effect
(mf/deps picking-color? picked-color-select)
(fn [] (when picking-color?
(on-change (:hex @current-color) (:alpha @current-color)))))
(fn [] (when (and picking-color? picked-color-select)
(on-change (:hex @current-color) (:alpha @current-color) picked-shift?))))
[:div.colorpicker {:ref ref-picker}
[:div.top-actions
@ -350,13 +354,24 @@
)
(mf/defc colorpicker-modal
{::mf/register modal/components
::mf/register-as :colorpicker}
[{:keys [x y default value opacity page on-change disable-opacity position on-accept] :as props}]
(let [position (or position :left)
style (case position
:left {:left (str (- x 270) "px")
:top (str (- y 50) "px")}
:right {:left (str (+ x 24) "px")
:top (str (- y 50) "px")})]
style (cond
(or (nil? x) (nil? y))
{:left "auto"
:right "16rem"
:top "4rem"}
(= position :left)
{:left (str (- x 270) "px")
:top (str (- y 50) "px")}
:else
{:left (str (+ x 24) "px")
:top (str (- y 50) "px")})
]
[:div.colorpicker-tooltip
{:style (clj->js style)}
[:& colorpicker {:value (or value default)

View file

@ -18,7 +18,6 @@
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
[app.main.ui.modal :as modal]
[app.main.ui.confirm :refer [confirm-dialog]]
[app.main.ui.workspace.presence :as presence]
[app.util.i18n :as i18n :refer [t]]
[app.util.data :refer [classnames]]
@ -63,7 +62,7 @@
add-shared-fn #(st/emit! nil (dw/set-file-shared (:id file) true))
on-add-shared
#(modal/show! confirm-dialog
#(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")
@ -72,7 +71,7 @@
remove-shared-fn #(st/emit! nil (dw/set-file-shared (:id file) false))
on-remove-shared
#(modal/show! confirm-dialog
#(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")

View file

@ -118,6 +118,8 @@
(mf/defc libraries-dialog
{::mf/register modal/components
::mf/register-as :libraries-dialog}
[{:keys [] :as ctx}]
(let [selected-tab (mf/use-state :libraries)

View file

@ -27,8 +27,6 @@
[app.main.ui.keyboard :as kbd]
[app.main.ui.modal :as modal]
[app.main.ui.shapes.icon :as icon]
[app.main.ui.workspace.libraries :refer [libraries-dialog]]
[app.main.ui.workspace.colorpicker :refer [colorpicker-modal]]
[app.util.data :refer [matches-search]]
[app.util.dom :as dom]
[app.util.dom.dnd :as dnd]
@ -170,7 +168,7 @@
edit-color-clicked
(fn [event]
(modal/show! colorpicker-modal
(modal/show! :colorpicker
{:x (.-clientX event)
:y (.-clientY event)
:on-accept edit-color
@ -235,7 +233,7 @@
(mf/use-callback
(mf/deps file-id)
(fn [event]
(modal/show! colorpicker-modal
(modal/show! :colorpicker
{:x (.-clientX event)
:y (.-clientY event)
:on-accept add-color
@ -372,7 +370,7 @@
[:div.tool-window-content
[:div.assets-bar-title
(t locale "workspace.assets.assets")
[:div.libraries-button {:on-click #(modal/show! libraries-dialog {})}
[:div.libraries-button {:on-click #(modal/show! :libraries-dialog {})}
i/libraries
(t locale "workspace.assets.libraries")]]

View file

@ -15,7 +15,6 @@
[app.util.data :refer [classnames]]
[app.util.i18n :as i18n :refer [tr]]
[app.main.ui.modal :as modal]
[app.main.ui.workspace.colorpicker :refer [colorpicker-modal]]
[app.common.data :as d]
[app.main.refs :as refs]))
@ -30,7 +29,7 @@
:value (:value color)
:opacity (:opacity color)
:disable-opacity disable-opacity}]
(modal/show! colorpicker-modal props))))
(modal/show! :colorpicker props))))
(defn value-to-background [value]
(if (= value :multiple) "transparent" value))
@ -142,12 +141,5 @@
:on-click select-all
:on-change handle-opacity-change
:min "0"
:max "100"}]])
#_[:input.slidebar {:type "range"
:min "0"
:max "100"
:value (-> opacity opacity->string)
:step "1"
:on-change handle-opacity-change}]]))
:max "100"}]])]))

View file

@ -16,7 +16,6 @@
[app.main.data.workspace.common :as dwc]
[app.main.store :as st]
[app.main.ui.icons :as i]
[app.main.ui.modal :as modal]
[app.main.ui.workspace.sidebar.options.rows.color-row :refer [color-row]]
[app.util.data :refer [classnames]]
[app.util.dom :as dom]

View file

@ -13,7 +13,6 @@
[app.main.data.workspace :as dw]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.confirm :refer [confirm-dialog]]
[app.main.ui.hooks :as hooks]
[app.main.ui.icons :as i]
[app.main.ui.keyboard :as kbd]
@ -34,7 +33,7 @@
id (:id page)
delete-fn (mf/use-callback (mf/deps id) #(st/emit! (dw/delete-page id)))
on-delete (mf/use-callback (mf/deps id) #(modal/show! confirm-dialog {:on-accept delete-fn}))
on-delete (mf/use-callback (mf/deps id) #(modal/show! :confirm-dialog {:on-accept delete-fn}))
navigate-fn (mf/use-callback (mf/deps id) #(st/emit! (dw/go-to-page id)))
on-double-click

View file

@ -450,14 +450,13 @@
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(st/emit! (dwc/pick-color-select true)))
(st/emit! (dwc/pick-color-select true (kbd/shift? event))))
on-mouse-up-picker
(fn [event]
(dom/prevent-default event)
(dom/stop-propagation event)
(st/emit! (dwc/pick-color-select false)
(dwc/stop-picker))
(st/emit! (dwc/stop-picker))
(modal/disallow-click-outside!))]
(mf/use-layout-effect