Quellcode durchsuchen

feat(ui): WIP add multi select component for shui

charlie vor 1 Jahr
Ursprung
Commit
77ecb5e8ae

+ 81 - 44
deps/shui/src/logseq/shui/demo2.cljs

@@ -2,11 +2,48 @@
   (:require [rum.core :as rum]
             [logseq.shui.ui :as ui]
             [logseq.shui.popup.core :refer [install-popups update-popup! get-popup]]
+            [logseq.shui.select.multi :refer [x-select]]
             [frontend.components.icon :refer [emojis-cp emojis icon-search]]))
 
 
 (rum/defc page
   []
+
+  [:div.sm:p-10
+   [:h1.text-3xl.font-bold.border-b.pb-4.mb-8
+    "Multi X Select"]
+
+   (let [items [{:key 1 :value "Apple"}
+                {:key 2 :value "Orange"}
+                {:key 3 :value "Pear"}]
+
+         [selected-items set-selected-items!]
+         (rum/use-state [(last items)])
+
+         on-chosen (fn [item {:keys [selected]}]
+                     (if (true? selected)
+                       (set-selected-items! (remove #(= item %) selected-items))
+                       (set-selected-items! (conj selected-items item))))]
+
+     (ui/card
+       (ui/card-header
+         (ui/card-title "Basic")
+         (ui/card-description "x multiselect for shui"))
+       (ui/card-content
+         (ui/dropdown-menu
+           {:open true}
+           ;; trigger
+           (ui/dropdown-menu-trigger
+             [:p.border.p-2.rounded
+              (for [{:keys [key value]} selected-items]
+                (ui/badge {:variant :outline} (str "#" key " " value)))
+              (ui/button {:variant :link :size :sm} "+")])
+           ;; content
+           (x-select items selected-items
+             {:on-chosen on-chosen})))))])
+
+(rum/defc icon-picker-demo
+  []
   [:div.sm:p-10
    [:h1.text-3xl.font-bold.border-b.pb-4.mb-8
     "UI X Emojis & Icons Picker"]
@@ -43,14 +80,14 @@
                          (set-emoji! t)
                          (ui/popup-hide-all!))})])
                  {:content-props {:class "w-72 p-0"}
-                  :as-menu?      true})}
+                  :as-menu? true})}
              (if emoji [:strong.px-1.text-6xl [:em-emoji emoji]] "emoji :O")] "."])]
      [:<>
       (emoji-picker nil)
 
       [:p.py-4
        (ui/button
-         {:variant  :secondary
+         {:variant :secondary
           :on-click #(ui/popup-show! %
                        (fn []
                          [:p.p-4
@@ -66,38 +103,38 @@
                 [:strong.px-1.text-6xl q]])]
          (ui/input
            {:placeholder "Select a fruit."
-            :ref         *q-ref
-            :value       q
-            :on-change   (fn [^js e]
-                           (let [val (.-value (.-target e))]
-                             (set-q! val)
-                             (update-popup! :select-a-fruit-input [:content] (gen-content val))))
-            :class       "w-1/5"
-            :on-focus    (fn [^js e]
-                           (let [id :select-a-fruit-input
-                                 [_ popup] (get-popup id)]
-                             (if (not popup)
-                               (ui/popup-show! (.-target e)
-                                 (gen-content q)
-                                 {:id id
-                                  :content-props
-                                  {:class           "x-input-popup-content"
-                                   :onPointerDownOutside
-                                   (fn [^js e]
-                                     (js/console.log "===>> onPointerDownOutside:" e (rum/deref *q-ref))
-                                     (when-let [q-ref (rum/deref *q-ref)]
-                                       (let [^js target (or (.-relatedTarget e)
-                                                          (.-target e))]
-                                         (js/console.log "t:" target)
-                                         (when (and
-                                                 (not (.contains q-ref target))
-                                                 (not (.closest target ".x-input-popup-content")))
-                                           (ui/popup-hide! id)))))
-                                   :onOpenAutoFocus #(.preventDefault %)}})
-
-                               ;; update content
-                               (update-popup! id [:content]
-                                 (gen-content q)))))
+            :ref *q-ref
+            :value q
+            :on-change (fn [^js e]
+                         (let [val (.-value (.-target e))]
+                           (set-q! val)
+                           (update-popup! :select-a-fruit-input [:content] (gen-content val))))
+            :class "w-1/5"
+            :on-focus (fn [^js e]
+                        (let [id :select-a-fruit-input
+                              [_ popup] (get-popup id)]
+                          (if (not popup)
+                            (ui/popup-show! (.-target e)
+                              (gen-content q)
+                              {:id id
+                               :content-props
+                               {:class "x-input-popup-content"
+                                :onPointerDownOutside
+                                (fn [^js e]
+                                  (js/console.log "===>> onPointerDownOutside:" e (rum/deref *q-ref))
+                                  (when-let [q-ref (rum/deref *q-ref)]
+                                    (let [^js target (or (.-relatedTarget e)
+                                                       (.-target e))]
+                                      (js/console.log "t:" target)
+                                      (when (and
+                                              (not (.contains q-ref target))
+                                              (not (.closest target ".x-input-popup-content")))
+                                        (ui/popup-hide! id)))))
+                                :onOpenAutoFocus #(.preventDefault %)}})
+
+                            ;; update content
+                            (update-popup! id [:content]
+                              (gen-content q)))))
             ;:on-blur     (fn [^js e]
             ;               (let [^js target (.-relatedTarget e)]
             ;                 (js/console.log "==>>>" target)
@@ -106,15 +143,15 @@
             }))]
 
       [:div.w-full.p-4.border.rounded.dotted.h-48.mt-8.bg-gray-02
-       {:on-click        #(ui/popup-show! %
-                            (->> (range 8)
-                              (map (fn [it]
-                                     (ui/dropdown-menu-item
-                                       {:on-select (fn []
-                                                     (ui/toast! it)
-                                                     (ui/popup-hide-all!))}
-                                       [:strong it]))))
-                            {:as-menu?      true
-                             :content-props {:class "w-48"}})
+       {:on-click #(ui/popup-show! %
+                     (->> (range 8)
+                       (map (fn [it]
+                              (ui/dropdown-menu-item
+                                {:on-select (fn []
+                                              (ui/toast! it)
+                                              (ui/popup-hide-all!))}
+                                [:strong it]))))
+                     {:as-menu? true
+                      :content-props {:class "w-48"}})
         :on-context-menu #(ui/popup-show! %
                             [:h1.text-3xl.font-bold "hi x popup for custom context menu!"])}]])])

+ 7 - 1
deps/shui/src/logseq/shui/form/core.cljs

@@ -50,4 +50,10 @@
 (def form-item (util/lsui-wrap "FormItem"))
 (def form-label (util/lsui-wrap "FormLabel"))
 (def form-description (util/lsui-wrap "FormDescription"))
-(def form-message (util/lsui-wrap "FormMessage"))
+(def form-message (util/lsui-wrap "FormMessage"))
+(def input (util/lsui-wrap "Input"))
+(def textarea (util/lsui-wrap "Textarea"))
+(def switch (util/lsui-wrap "Switch"))
+(def checkbox (util/lsui-wrap "Checkbox"))
+(def radio-group (util/lsui-wrap "RadioGroup"))
+(def radio-group-item (util/lsui-wrap "RadioGroupItem"))

+ 40 - 0
deps/shui/src/logseq/shui/select/multi.cljs

@@ -0,0 +1,40 @@
+(ns logseq.shui.select.multi
+  (:require [rum.core :as rum]
+            [logseq.shui.popup.core :as popup]
+            [logseq.shui.form.core :as form]))
+
+(defn get-k [item]
+  (when (map? item)
+    (some->> ((juxt :id :key :label) item)
+      (remove nil?)
+      (first))))
+
+(rum/defc x-select
+  [items selected-items & {:keys [on-chosen item-render item-props
+                                  content-props]}]
+  (let [x-content popup/dropdown-menu-content
+        x-item popup/dropdown-menu-item]
+    (x-content
+      (merge {} content-props)
+      (for [item items
+            :let [selected? (some #(let [k (get-k item)
+                                         k' (get-k %)]
+                                     (or (= item %)
+                                       (and (not (nil? k))
+                                         (not (nil? k'))
+                                         (= k k'))))
+                              selected-items)]]
+        (if (fn? item-render)
+          (item-render item {:x-item x-item :selected selected?})
+          (let [{:keys [title value]} item
+                k (get-k item)
+                v (or title value)
+                on-click' (:on-click item-props)
+                on-click (fn [e]
+                           ;; TODO: return value
+                           (when (fn? on-click') (on-click' e))
+                           (when (fn? on-chosen)
+                             (on-chosen item {:selected selected?})))]
+            (x-item (merge {:data-k k :on-click on-click} item-props)
+              [:a.flex.items-center.gap-2.w-full
+               (form/checkbox {:checked selected?}) v])))))))

+ 6 - 6
deps/shui/src/logseq/shui/ui.cljs

@@ -16,14 +16,14 @@
 (def alert-description (util/lsui-wrap "AlertDescription"))
 (def slider (util/lsui-wrap "Slider"))
 (def badge (util/lsui-wrap "Badge"))
-(def input (util/lsui-wrap "Input"))
-(def textarea (util/lsui-wrap "Textarea"))
-(def switch (util/lsui-wrap "Switch"))
-(def checkbox (util/lsui-wrap "Checkbox"))
-(def radio-group (util/lsui-wrap "RadioGroup"))
-(def radio-group-item (util/lsui-wrap "RadioGroupItem"))
 (def skeleton (util/lsui-wrap "Skeleton"))
 (def calendar (util/lsui-wrap "Calendar"))
+(def input (form-core/input))
+(def textarea (form-core/textarea))
+(def switch (form-core/switch))
+(def checkbox (form-core/checkbox))
+(def radio-group (form-core/radio-group))
+(def radio-group-item (form-core/radio-group-item))
 (def popover popup-core/popover)
 (def popover-trigger popup-core/popover-trigger)
 (def popover-content popup-core/popover-content)