|
|
@@ -1,51 +1,98 @@
|
|
|
(ns logseq.shui.demo2
|
|
|
(:require [rum.core :as rum]
|
|
|
[logseq.shui.ui :as ui]
|
|
|
- [frontend.rum :refer [use-atom]]
|
|
|
+ [medley.core :as medley]
|
|
|
+ [logseq.shui.util :refer [use-atom]]
|
|
|
[frontend.components.icon :refer [emojis-cp emojis]]))
|
|
|
|
|
|
(defonce *x-popup-state
|
|
|
(atom {:open? false :content nil :position [0 0]}))
|
|
|
|
|
|
+;; {:id "" :open? false :content nil :position [0 0] :root-props nil :content-props nil}
|
|
|
+(defonce ^:private *popups (atom []))
|
|
|
+(defonce ^:private *id (atom 0))
|
|
|
+(defonce ^:private gen-id #(reset! *id (inc @*id)))
|
|
|
+
|
|
|
+(defn get-popup
|
|
|
+ [id]
|
|
|
+ (when id
|
|
|
+ (some->> (medley/indexed @*popups)
|
|
|
+ (filter #(= id (:id (second %)))) (first))))
|
|
|
+
|
|
|
+(defn upsert-popup!
|
|
|
+ [config]
|
|
|
+ (when-let [_id (:id config)]
|
|
|
+ (swap! *popups conj config)))
|
|
|
+
|
|
|
+(defn update-popup!
|
|
|
+ [id ks val]
|
|
|
+ (when-let [[index config] (get-popup id)]
|
|
|
+ (let [ks (if (coll? ks) ks [ks])
|
|
|
+ config (if (nil? val)
|
|
|
+ (medley/dissoc-in config ks)
|
|
|
+ (assoc-in config ks val))]
|
|
|
+ (swap! *popups assoc index config))))
|
|
|
+
|
|
|
+(defn detach-popup!
|
|
|
+ [id]
|
|
|
+ (when-let [[index] (get-popup id)]
|
|
|
+ (swap! *popups #(->> % (medley/remove-nth index) (vec)))))
|
|
|
+
|
|
|
(defn show-x-popup!
|
|
|
- [^js event content & {:keys [as-menu? root-props content-props]}]
|
|
|
+ [^js event content & {:keys [id as-menu? root-props content-props]}]
|
|
|
(let [x (.-clientX event)
|
|
|
y (.-clientY event)]
|
|
|
- (reset! *x-popup-state
|
|
|
- {:open? true :content content :position [x y]
|
|
|
+ (upsert-popup!
|
|
|
+ {:id (or id (gen-id)) :open? true :content content :position [x y]
|
|
|
:as-menu? as-menu? :root-props root-props :content-props content-props})))
|
|
|
|
|
|
(defn hide-x-popup!
|
|
|
+ [id]
|
|
|
+ (update-popup! id :open? false))
|
|
|
+
|
|
|
+(defn hide-x-popup-all!
|
|
|
+ []
|
|
|
+ (doseq [{:keys [id]} @*popups]
|
|
|
+ (hide-x-popup! id)))
|
|
|
+
|
|
|
+(rum/defc x-popup [{:keys [id open? content position as-menu? root-props content-props] :as _props}]
|
|
|
+ (rum/use-effect!
|
|
|
+ (fn []
|
|
|
+ (when (false? open?)
|
|
|
+ (js/setTimeout #(detach-popup! id) 128)))
|
|
|
+ [open?])
|
|
|
+
|
|
|
+ (when-let [[x y] position]
|
|
|
+ (let [popup-root (if as-menu? ui/dropdown-menu ui/popover)
|
|
|
+ popup-trigger (if as-menu? ui/dropdown-menu-trigger ui/popover-trigger)
|
|
|
+ popup-content (if as-menu? ui/dropdown-menu-content ui/popover-content)]
|
|
|
+ (popup-root
|
|
|
+ (merge root-props {:open open?})
|
|
|
+ (popup-trigger
|
|
|
+ {:as-child true}
|
|
|
+ (ui/button {:class "w-1 h-1 overflow-hidden fixed p-0 opacity-0"
|
|
|
+ :style {:top y :left x}} ""))
|
|
|
+ (popup-content
|
|
|
+ (merge {:onEscapeKeyDown #(hide-x-popup! id)
|
|
|
+ :onPointerDownOutside #(hide-x-popup! id)} content-props)
|
|
|
+ (if (fn? content) (content {:id id}) content))))))
|
|
|
+
|
|
|
+(rum/defc install-popups
|
|
|
+ < rum/static
|
|
|
[]
|
|
|
- (when (true? (:open? @*x-popup-state))
|
|
|
- (swap! *x-popup-state assoc :open? false)
|
|
|
- (js/setTimeout
|
|
|
- #(reset! *x-popup-state
|
|
|
- {:open? false :content nil :position [0 0] :as-menu? false}) 128)))
|
|
|
-
|
|
|
-(rum/defc x-popup []
|
|
|
- (let [[{:keys [open? content position as-menu? root-props content-props]} _] (use-atom *x-popup-state)]
|
|
|
- (when-let [[x y] position]
|
|
|
- (let [popup-root (if as-menu? ui/dropdown-menu ui/popover)
|
|
|
- popup-trigger (if as-menu? ui/dropdown-menu-trigger ui/popover-trigger)
|
|
|
- popup-content (if as-menu? ui/dropdown-menu-content ui/popover-content)]
|
|
|
- (popup-root
|
|
|
- (merge root-props {:open open?})
|
|
|
- (popup-trigger
|
|
|
- {:as-child true}
|
|
|
- (ui/button {:class "w-1 h-1 overflow-hidden fixed p-0 opacity-0"
|
|
|
- :style {:top y :left x}} ""))
|
|
|
- (popup-content
|
|
|
- (merge {:onEscapeKeyDown #(hide-x-popup!)
|
|
|
- :onPointerDownOutside #(hide-x-popup!)} content-props)
|
|
|
- (if (fn? content) (content) content))))
|
|
|
- )))
|
|
|
+ (let [[popups _set-popups!] (use-atom *popups)]
|
|
|
+ [:<>
|
|
|
+ (for [config popups
|
|
|
+ :when (and (map? config) (:id config))]
|
|
|
+ (x-popup config))]))
|
|
|
|
|
|
(rum/defc page []
|
|
|
[:div.sm:p-10
|
|
|
[:h1.text-3xl.font-bold.border-b.pb-4 "UI X Popup"]
|
|
|
|
|
|
- (rum/portal (x-popup) js/document.body)
|
|
|
+ (rum/portal
|
|
|
+ (install-popups)
|
|
|
+ js/document.body)
|
|
|
|
|
|
(let [[emoji set-emoji!] (rum/use-state nil)
|
|
|
emoji-picker (fn [_nested?]
|
|
|
@@ -58,7 +105,7 @@
|
|
|
{:on-chosen
|
|
|
(fn [_ t]
|
|
|
(set-emoji! t)
|
|
|
- (hide-x-popup!))})]
|
|
|
+ (hide-x-popup-all!))})]
|
|
|
{:content-props {:class "w-72 p-0"}
|
|
|
:as-menu? true})}
|
|
|
(if emoji [:strong.px-1.text-6xl [:em-emoji emoji]] "emoji :O")] "."])]
|
|
|
@@ -81,7 +128,7 @@
|
|
|
(ui/dropdown-menu-item
|
|
|
{:on-select (fn []
|
|
|
(ui/toast! it)
|
|
|
- (hide-x-popup!))}
|
|
|
+ (hide-x-popup-all!))}
|
|
|
[:strong it]))))
|
|
|
{:as-menu? true
|
|
|
:content-props {:class "w-48"}})
|