Tienson Qin 1 год назад
Родитель
Сommit
6505d535c3

+ 450 - 437
deps/shui/src/logseq/shui/demo.cljs

@@ -16,475 +16,488 @@
   []
   (let [icon #(ui/tabler-icon (name %1) {:class "scale-90 pr-1 opacity-80"})]
     (ui/dropdown-menu-content
-      {:class "w-56"
-       :on-click (fn [^js e] (some-> (.-target e) (.-innerText)
-                               (#(identity ["You select: " [:b.text-red-700 %1]])) (ui/toast! :info)))}
-      (ui/dropdown-menu-label "My Account")
-      (ui/dropdown-menu-separator)
-      (ui/dropdown-menu-group
+     {:class "w-56"
+      :on-click (fn [^js e] (some-> (.-target e) (.-innerText)
+                                    (#(identity ["You select: " [:b.text-red-700 %1]])) (ui/toast! :info)))}
+     (ui/dropdown-menu-label "My Account")
+     (ui/dropdown-menu-separator)
+     (ui/dropdown-menu-group
         ;; items
-        (ui/dropdown-menu-item (icon :user) "Profile" (ui/dropdown-menu-shortcut "⌘P"))
-        (ui/dropdown-menu-item (icon :brand-mastercard) [:span "Billing"] (ui/dropdown-menu-shortcut "⌘B"))
-        (ui/dropdown-menu-item (icon :adjustments-alt) [:span "Settings"] (ui/dropdown-menu-shortcut "⌘,"))
-        (ui/dropdown-menu-item (icon :keyboard) [:span "Keyboard shortcuts"]))
-      (ui/dropdown-menu-separator)
+      (ui/dropdown-menu-item (icon :user) "Profile" (ui/dropdown-menu-shortcut "⌘P"))
+      (ui/dropdown-menu-item (icon :brand-mastercard) [:span "Billing"] (ui/dropdown-menu-shortcut "⌘B"))
+      (ui/dropdown-menu-item (icon :adjustments-alt) [:span "Settings"] (ui/dropdown-menu-shortcut "⌘,"))
+      (ui/dropdown-menu-item (icon :keyboard) [:span "Keyboard shortcuts"]))
+     (ui/dropdown-menu-separator)
       ;; group
-      (ui/dropdown-menu-group
+     (ui/dropdown-menu-group
         ;; items
-        (ui/dropdown-menu-item (icon :users) "Team")
+      (ui/dropdown-menu-item (icon :users) "Team")
         ;; sub menu
-        (ui/dropdown-menu-sub
-          (ui/dropdown-menu-sub-trigger
-            (icon :user-plus) [:span "Invite users"])
-          (ui/dropdown-menu-sub-content
-            (ui/dropdown-menu-item (icon :mail) "Email")
-            (ui/dropdown-menu-item (icon :message) "Message")
-            (ui/dropdown-menu-item (icon :dots-circle-horizontal) "More...")))
+      (ui/dropdown-menu-sub
+       (ui/dropdown-menu-sub-trigger
+        (icon :user-plus) [:span "Invite users"])
+       (ui/dropdown-menu-sub-content
+        (ui/dropdown-menu-item (icon :mail) "Email")
+        (ui/dropdown-menu-item (icon :message) "Message")
+        (ui/dropdown-menu-item (icon :dots-circle-horizontal) "More...")))
         ;; menu item
-        (ui/dropdown-menu-item (icon :plus) "New Team" (ui/dropdown-menu-shortcut "⌘+T")))
-      (ui/dropdown-menu-separator)
-      (ui/dropdown-menu-item (icon :brand-github) "GitHub")
-      (ui/dropdown-menu-item {:disabled true} (icon :cloud) "Cloud API")
-      (ui/dropdown-menu-separator)
-      (ui/dropdown-menu-item (icon :logout) "Logout" (ui/dropdown-menu-shortcut "⌘+Q"))
-      )))
+      (ui/dropdown-menu-item (icon :plus) "New Team" (ui/dropdown-menu-shortcut "⌘+T")))
+     (ui/dropdown-menu-separator)
+     (ui/dropdown-menu-item (icon :brand-github) "GitHub")
+     (ui/dropdown-menu-item {:disabled true} (icon :cloud) "Cloud API")
+     (ui/dropdown-menu-separator)
+     (ui/dropdown-menu-item (icon :logout) "Logout" (ui/dropdown-menu-shortcut "⌘+Q")))))
 
 (rum/defc sample-context-menu-content
   []
   (let [icon #(ui/tabler-icon (name %1) {:class "scale-90 pr-1 opacity-80"})]
     (ui/context-menu
       ;; trigger
-      (ui/context-menu-trigger
-        [:div.border.px-6.py-12.border-dashed.rounded.text-center.select-none
-         {:key "ctx-menu-click"}
-         [:span.opacity-50 "Right click here"]])
+     (ui/context-menu-trigger
+      [:div.border.px-6.py-12.border-dashed.rounded.text-center.select-none
+       {:key "ctx-menu-click"}
+       [:span.opacity-50 "Right click here"]])
       ;; content
-      (ui/context-menu-content
-        {:class "w-60 max-h-[80vh] overflow-auto"}
-        (ui/context-menu-item
-          (icon "arrow-left")
-          "Back"
-          (ui/context-menu-shortcut "⌘["))
-        (ui/context-menu-item {:disabled true}
-          (icon "arrow-right")
-          "Forward"
-          (ui/context-menu-shortcut "⌘]"))
-        (ui/context-menu-item
-          (icon "refresh")
-          "Reload"
-          (ui/context-menu-shortcut "⌘R"))
+     (ui/context-menu-content
+      {:class "w-60 max-h-[80vh] overflow-auto"}
+      (ui/context-menu-item
+       (icon "arrow-left")
+       "Back"
+       (ui/context-menu-shortcut "⌘["))
+      (ui/context-menu-item {:disabled true}
+                            (icon "arrow-right")
+                            "Forward"
+                            (ui/context-menu-shortcut "⌘]"))
+      (ui/context-menu-item
+       (icon "refresh")
+       "Reload"
+       (ui/context-menu-shortcut "⌘R"))
         ;; Sub menu
-        (ui/context-menu-sub
-          (ui/context-menu-sub-trigger {:inset true} "More tools")
-          (ui/context-menu-sub-content {:class "w-48"}
-            (ui/context-menu-item "Save page As..."
-              (ui/context-menu-shortcut "⇧⌘S"))
-            (ui/context-menu-item "Create Shortcut...")
-            (ui/context-menu-item "Name Window...")
-            (ui/context-menu-separator)
-            (ui/context-menu-item "Developer Tools")))
+      (ui/context-menu-sub
+       (ui/context-menu-sub-trigger {:inset true} "More tools")
+       (ui/context-menu-sub-content {:class "w-48"}
+                                    (ui/context-menu-item "Save page As..."
+                                                          (ui/context-menu-shortcut "⇧⌘S"))
+                                    (ui/context-menu-item "Create Shortcut...")
+                                    (ui/context-menu-item "Name Window...")
+                                    (ui/context-menu-separator)
+                                    (ui/context-menu-item "Developer Tools")))
         ;; more
-        (ui/context-menu-separator)
-        (ui/context-menu-checkbox-item {:checked true}
-          "Show Bookmarks Bar" (ui/context-menu-shortcut "⌘⇧B"))
-        (ui/context-menu-checkbox-item "Show Full URLs")
-        (ui/context-menu-separator)
-        (ui/context-menu-radio-group {:value "pedro"}
-          (ui/context-menu-label {:inset true} "People")
-          (ui/context-menu-separator)
-          (ui/context-menu-radio-item {:value "pedro"} "Pedro Duarte")
-          (ui/context-menu-radio-item {:value "colm"} "Colm Tuite"))))))
+      (ui/context-menu-separator)
+      (ui/context-menu-checkbox-item {:checked true}
+                                     "Show Bookmarks Bar" (ui/context-menu-shortcut "⌘⇧B"))
+      (ui/context-menu-checkbox-item "Show Full URLs")
+      (ui/context-menu-separator)
+      (ui/context-menu-radio-group {:value "pedro"}
+                                   (ui/context-menu-label {:inset true} "People")
+                                   (ui/context-menu-separator)
+                                   (ui/context-menu-radio-item {:value "pedro"} "Pedro Duarte")
+                                   (ui/context-menu-radio-item {:value "colm"} "Colm Tuite"))))))
+
+(rum/defc sample-tabs
+  []
+  (ui/tabs
+   {:defaultValue "account"
+    :className "w-[400px]"}
+   (ui/tabs-list
+    (ui/tabs-trigger
+     {:value "account"}
+     "Account")
+    (ui/tabs-trigger
+     {:value "password"}
+     "Password"))
+   (ui/tabs-content
+    {:value "account"}
+    "Make changes to your account here.")
+   (ui/tabs-content
+    {:value "password"}
+    "Change your password here.")))
 
 (rum/defc sample-form-basic
   []
   [:div.border.p-6.rounded.bg-gray-01
    (let [form-ctx (form-core/use-form
-                    {:defaultValues {:username ""
-                                     :agreement true
-                                     :notification "all"
-                                     :bio ""}
-                     :yupSchema (-> (.object yup)
-                                  (.shape #js {:username (-> (.string yup) (.required))})
-                                  (.required))})
+                   {:defaultValues {:username ""
+                                    :agreement true
+                                    :notification "all"
+                                    :bio ""}
+                    :yupSchema (-> (.object yup)
+                                   (.shape #js {:username (-> (.string yup) (.required))})
+                                   (.required))})
          handle-submit (:handleSubmit form-ctx)
          on-submit-valid (handle-submit
-                           (fn [^js e]
-                             (js/console.log "[form] submit: " e)
-                             (js/alert (js/JSON.stringify e nil 2))))]
+                          (fn [^js e]
+                            (js/console.log "[form] submit: " e)
+                            (js/alert (js/JSON.stringify e nil 2))))]
 
      (ui/form-provider form-ctx
-       [:form
-        {:on-submit on-submit-valid}
+                       [:form
+                        {:on-submit on-submit-valid}
 
         ;; field item
-        (ui/form-field {:name "username"}
-          (fn [field error]
-            (ui/form-item
-              (ui/form-label "Username")
-              (ui/form-control
-                (ui/input (merge {:placeholder "Username"} field)))
-              (ui/form-description
-                (if error
-                  [:b.text-red-800 (:message error)]
-                  "This is your public display name.")))))
-
-        (ui/form-field {:name "bio"}
-          (fn [field error]
-            (ui/form-item
-              {:class "pt-4"}
-              (ui/form-control
-                (ui/textarea (merge {:placeholder "Bio text..."} field))))))
+                        (ui/form-field {:name "username"}
+                                       (fn [field error]
+                                         (ui/form-item
+                                          (ui/form-label "Username")
+                                          (ui/form-control
+                                           (ui/input (merge {:placeholder "Username"} field)))
+                                          (ui/form-description
+                                           (if error
+                                             [:b.text-red-800 (:message error)]
+                                             "This is your public display name.")))))
+
+                        (ui/form-field {:name "bio"}
+                                       (fn [field error]
+                                         (ui/form-item
+                                          {:class "pt-4"}
+                                          (ui/form-control
+                                           (ui/textarea (merge {:placeholder "Bio text..."} field))))))
 
         ;; radio
-        (ui/form-field {:name "notification"}
+                        (ui/form-field {:name "notification"}
           ;; item render
-          (fn [field]
-            (ui/form-item
-              {:class "space-y-3 my-4"}
-              (ui/form-label "Notify me about...")
-              (ui/form-control
-                (ui/radio-group
-                  {:value (:value field)
-                   :on-value-change (:onChange field)
-                   :class "flex flex-col space-y-3"}
-                  (ui/form-item
-                    {:class "flex flex-row space-x-3 items-center space-y-0"}
-                    (ui/form-control
-                      (ui/radio-group-item {:value "all"}))
-                    (ui/form-label "All"))
-
-                  (ui/form-item
-                    {:class "flex flex-row space-x-3 items-center space-y-0"}
-                    (ui/form-control
-                      (ui/radio-group-item {:value "direct"}))
-                    (ui/form-label "Direct messages and mentions")))))))
-
-        [:hr]
+                                       (fn [field]
+                                         (ui/form-item
+                                          {:class "space-y-3 my-4"}
+                                          (ui/form-label "Notify me about...")
+                                          (ui/form-control
+                                           (ui/radio-group
+                                            {:value (:value field)
+                                             :on-value-change (:onChange field)
+                                             :class "flex flex-col space-y-3"}
+                                            (ui/form-item
+                                             {:class "flex flex-row space-x-3 items-center space-y-0"}
+                                             (ui/form-control
+                                              (ui/radio-group-item {:value "all"}))
+                                             (ui/form-label "All"))
+
+                                            (ui/form-item
+                                             {:class "flex flex-row space-x-3 items-center space-y-0"}
+                                             (ui/form-control
+                                              (ui/radio-group-item {:value "direct"}))
+                                             (ui/form-label "Direct messages and mentions")))))))
+
+                        [:hr]
 
         ;; checkbox
-        (ui/form-field {:name "agreement"}
-          (fn [field]
-            (ui/form-item
-              {:class "flex justify-start items-center space-x-3 space-y-0 my-3 pr-3"}
-              (ui/form-control
-                (ui/checkbox {:checked (:value field)
-                              :on-checked-change (:onChange field)}))
-              (ui/form-label {:class "font-normal cursor-pointer"} "Agreement terms"))))
+                        (ui/form-field {:name "agreement"}
+                                       (fn [field]
+                                         (ui/form-item
+                                          {:class "flex justify-start items-center space-x-3 space-y-0 my-3 pr-3"}
+                                          (ui/form-control
+                                           (ui/checkbox {:checked (:value field)
+                                                         :on-checked-change (:onChange field)}))
+                                          (ui/form-label {:class "font-normal cursor-pointer"} "Agreement terms"))))
 
         ;; actions
-        [:div.relative.px-2
-         (ui/button {:type "submit" :class "!absolute right-0 top-[-40px]"} "Submit")]]))])
+                        [:div.relative.px-2
+                         (ui/button {:type "submit" :class "!absolute right-0 top-[-40px]"} "Submit")]]))])
 
 (rum/defc sample-date-picker
   []
   (let [[open? set-open!] (rum/use-state false)
         [date set-date!] (rum/use-state (js/Date.))]
     (ui/popover
-      {:open open?
-       :on-open-change (fn [o] (set-open! o))}
+     {:open open?
+      :on-open-change (fn [o] (set-open! o))}
       ;; trigger
-      (ui/popover-trigger
-        {:as-child true
-         :class "w-2/3"}
-        (ui/input
-          {:type :text
-           :placeholder "pick a date"
-           :default-value (.toDateString date)}))
+     (ui/popover-trigger
+      {:as-child true
+       :class "w-2/3"}
+      (ui/input
+       {:type :text
+        :placeholder "pick a date"
+        :default-value (.toDateString date)}))
       ;; content
-      (ui/popover-content
-        {:on-open-auto-focus #(.preventDefault %)
-         :side-offset 8
-         :class "p-0"}
-        (ui/calendar
-          {:selected date
-           :on-day-click
-           (fn [^js d]
-             (set-date! d)
-             (set-open! false))})))))
+     (ui/popover-content
+      {:on-open-auto-focus #(.preventDefault %)
+       :side-offset 8
+       :class "p-0"}
+      (ui/calendar
+       {:selected date
+        :on-day-click
+        (fn [^js d]
+          (set-date! d)
+          (set-open! false))})))))
 
 (rum/defc sample-dialog-basic
   []
   (let [[open? set-open!] (rum/use-state false)]
     (ui/dialog
-      {:open open?
-       :on-open-change #(set-open! %)}
-      (ui/dialog-trigger
-        {:as-child true}
-        (ui/button {:variant :outline}
-          (ui/tabler-icon "notification") "Open as modal locally"))
-      (ui/dialog-content
-        (ui/dialog-header
-          (ui/dialog-title "Header")
-          (ui/dialog-description
-            "Description"))
-        [:div.max-h-96.overflow-y-auto
-         {:class "-mx-6"}
-         [:section.px-6
-          (repeat 8 [:p "Your custom content"])]]
-        (ui/dialog-footer
-          (ui/button
-            {:on-click #(set-open! false)
-             :size :md} "🍄 * Footer"))))))
-
+     {:open open?
+      :on-open-change #(set-open! %)}
+     (ui/dialog-trigger
+      {:as-child true}
+      (ui/button {:variant :outline}
+                 (ui/tabler-icon "notification") "Open as modal locally"))
+     (ui/dialog-content
+      (ui/dialog-header
+       (ui/dialog-title "Header")
+       (ui/dialog-description
+        "Description"))
+      [:div.max-h-96.overflow-y-auto
+       {:class "-mx-6"}
+       [:section.px-6
+        (repeat 8 [:p "Your custom content"])]]
+      (ui/dialog-footer
+       (ui/button
+        {:on-click #(set-open! false)
+         :size :md} "🍄 * Footer"))))))
 
 (rum/defc page []
   (ui/tooltip-provider
-    [:div.sm:p-10
-     [:hr]
-     [:input
-      {:type "checkbox" :on-change #(js/console.log "===>> onChange:" % (.-value (.-target %)))}]
-     (ui/checkbox {:on-click
-                   (fn [^js e] (js/console.log "==>> click:"
-                                 (set! (. (.-target e) -checked) (.-state (.-dataset (.-target e))))
-                                 (.-checked (.-target e))
-                                 ))
-                   :on-checked-change #(js/console.log "==>> on checked change:" %)
-                   } "abc")
-
-     [:h1.text-3xl.font-bold "Logseq UI"]
-     [:hr]
+   [:div.sm:p-10
+    [:hr]
+    [:input
+     {:type "checkbox" :on-change #(js/console.log "===>> onChange:" % (.-value (.-target %)))}]
+    (ui/checkbox {:on-click
+                  (fn [^js e] (js/console.log "==>> click:"
+                                              (set! (. (.-target e) -checked) (.-state (.-dataset (.-target e))))
+                                              (.-checked (.-target e))))
+                  :on-checked-change #(js/console.log "==>> on checked change:" %)} "abc")
+
+    [:h1.text-3xl.font-bold "Logseq UI"]
+    [:hr]
 
      ;; Button
-     (section-item "Button"
-       [:div.flex.flex-row.flex-wrap.gap-2
-        (let [[loading? set-loading!] (rum/use-state false)]
-          (ui/button
-            {:size :sm
-             :on-click (fn []
-                         (set-loading! true)
-                         (js/setTimeout #(set-loading! false) 5000))
-             :disabled loading?}
-            (when loading?
-              (ui/tabler-icon "loader2" {:class "animate-spin"}))
-            "Logseq Classic Button"
-            (ui/tabler-icon "arrow-right")))
-
-        (ui/button {:variant :outline :size :sm} "Outline")
-        (ui/button {:variant :secondary :size :sm} "Secondary")
-        (ui/button {:disabled true :size :sm} "Disabled")
-        (ui/button {:variant :destructive :size :sm} "Destructive")
-        (ui/button {:class "primary-green" :size :sm} "Custom (.primary-green)")
-        (ui/button {:variant :ghost :size :sm} "Ghost")
-        (ui/button {:variant :link :size :sm} "Link")
-        (ui/button
-          {:variant :icon
-           :size :sm}
-          [:a.flex.items-center.text-blue-rx-10.hover:text-blue-rx-10-alpha
-           {:href "https://x.com/logseq" :target "_blank"}
-           (ui/tabler-icon "brand-twitter" {:size 15})]
-          )])
-
-     ;; Toast
-     (section-item "Toast"
-       [:div.flex.flex-row.flex-wrap.gap-2
-        (ui/button
-          {:size :md
-           :variant :outline
-           :on-click #(ui/toast!
-                        "Check for updates ..."
-                        (nth [:success :error :default :info :warning] (rand-int 3))
-                        {:title (if (odd? (js/Date.now)) "History of China" "")
-                         :duration 3000})}
-          "Open random toast"
-          (ui/tabler-icon "arrow-right"))
-
-        (ui/button
-          {:variant :secondary
-           :size :md
-           :on-click (fn []
-                       (ui/toast!
-                         (fn [{:keys [id dismiss! update!]}]
-                           [:b.text-red-700
-                            [:div.flex.items-center.gap-2
-                             (ui/tabler-icon "info-circle")
-                             (str "#(" id ") ")
-                             (.toLocaleString (js/Date.))]
-                            [:div.flex.flex-row.gap-2
-                             (ui/button
-                               {:on-click #(dismiss! id) :size :sm}
-                               "x close")
-
-                             (ui/button
-                               {:on-click #(update! {:title (js/Date.now)
-                                                     :action [:b (ui/button {:on-click (fn [] (ui/toast-dismiss!))} "clear all")]})
-                                :size :sm}
-                               "x update")]])
-                         :default
-                         {:duration 3000 :onDismiss #(js/console.log "===>> dismiss?:" %1)}))}
-          (ui/tabler-icon "apps")
-          "Toast callback handle")
-
-        (ui/button
-          {:on-click #(ui/toast! "A message from SoundCloud..."
-                        {:class "text-orange-rx-10"
-                         :icon [:b.pl-1 (ui/tabler-icon "brand-soundcloud" {:size 20})]
-                         :duration 3000})
-           :class "primary-orange"
-           :size :md}
-          "Custom icon")])
-
-     [:div.flex.flex-row.space-x-16.items-center
+    (section-item "Button"
+                  [:div.flex.flex-row.flex-wrap.gap-2
+                   (let [[loading? set-loading!] (rum/use-state false)]
+                     (ui/button
+                      {:size :sm
+                       :on-click (fn []
+                                   (set-loading! true)
+                                   (js/setTimeout #(set-loading! false) 5000))
+                       :disabled loading?}
+                      (when loading?
+                        (ui/tabler-icon "loader2" {:class "animate-spin"}))
+                      "Logseq Classic Button"
+                      (ui/tabler-icon "arrow-right")))
+
+                   (ui/button {:variant :outline :size :sm} "Outline")
+                   (ui/button {:variant :secondary :size :sm} "Secondary")
+                   (ui/button {:disabled true :size :sm} "Disabled")
+                   (ui/button {:variant :destructive :size :sm} "Destructive")
+                   (ui/button {:class "primary-green" :size :sm} "Custom (.primary-green)")
+                   (ui/button {:variant :ghost :size :sm} "Ghost")
+                   (ui/button {:variant :link :size :sm} "Link")
+                   (ui/button
+                    {:variant :icon
+                     :size :sm}
+                    [:a.flex.items-center.text-blue-rx-10.hover:text-blue-rx-10-alpha
+                     {:href "https://x.com/logseq" :target "_blank"}
+                     (ui/tabler-icon "brand-twitter" {:size 15})])])
+
+;; Toast
+    (section-item "Toast"
+                  [:div.flex.flex-row.flex-wrap.gap-2
+                   (ui/button
+                    {:size :md
+                     :variant :outline
+                     :on-click #(ui/toast!
+                                 "Check for updates ..."
+                                 (nth [:success :error :default :info :warning] (rand-int 3))
+                                 {:title (if (odd? (js/Date.now)) "History of China" "")
+                                  :duration 3000})}
+                    "Open random toast"
+                    (ui/tabler-icon "arrow-right"))
+
+                   (ui/button
+                    {:variant :secondary
+                     :size :md
+                     :on-click (fn []
+                                 (ui/toast!
+                                  (fn [{:keys [id dismiss! update!]}]
+                                    [:b.text-red-700
+                                     [:div.flex.items-center.gap-2
+                                      (ui/tabler-icon "info-circle")
+                                      (str "#(" id ") ")
+                                      (.toLocaleString (js/Date.))]
+                                     [:div.flex.flex-row.gap-2
+                                      (ui/button
+                                       {:on-click #(dismiss! id) :size :sm}
+                                       "x close")
+
+                                      (ui/button
+                                       {:on-click #(update! {:title (js/Date.now)
+                                                             :action [:b (ui/button {:on-click (fn [] (ui/toast-dismiss!))} "clear all")]})
+                                        :size :sm}
+                                       "x update")]])
+                                  :default
+                                  {:duration 3000 :onDismiss #(js/console.log "===>> dismiss?:" %1)}))}
+                    (ui/tabler-icon "apps")
+                    "Toast callback handle")
+
+                   (ui/button
+                    {:on-click #(ui/toast! "A message from SoundCloud..."
+                                           {:class "text-orange-rx-10"
+                                            :icon [:b.pl-1 (ui/tabler-icon "brand-soundcloud" {:size 20})]
+                                            :duration 3000})
+                     :class "primary-orange"
+                     :size :md}
+                    "Custom icon")])
+
+    [:div.flex.flex-row.space-x-16.items-center
       ;; Tips
-      (section-item "Tips"
-        [:div.flex.flex-row.flex-wrap.gap-2
-         (ui/tooltip-provider
-           (ui/tooltip
-             (ui/tooltip-trigger
-               (ui/button
-                 {:variant :outline
-                  :on-click #(dialog-core/open! [:h1.text-9xl.text-center.scale-110 "🍄"])}
-                 "Tip for hint?"))
-             (ui/tooltip-content
-               {:class "w-42 px-8 py-4 text-xl border-green-rx-08 bg-green-rx-07-alpha"}
-               "🍄")))])
+     (section-item "Tips"
+                   [:div.flex.flex-row.flex-wrap.gap-2
+                    (ui/tooltip-provider
+                     (ui/tooltip
+                      (ui/tooltip-trigger
+                       (ui/button
+                        {:variant :outline
+                         :on-click #(dialog-core/open! [:h1.text-9xl.text-center.scale-110 "🍄"])}
+                        "Tip for hint?"))
+                      (ui/tooltip-content
+                       {:class "w-42 px-8 py-4 text-xl border-green-rx-08 bg-green-rx-07-alpha"}
+                       "🍄")))])
       ;; Avatar
-      (section-item "Avatar"
-        [:div.flex.flex-row.space-x-6.items-center
-         (ui/avatar
-           (ui/avatar-image {:src "https://avatars.githubusercontent.com/u/63385289?s=200&v=4"})
-           (ui/avatar-fallback "L"))
-         (ui/avatar
-           (ui/avatar-fallback "CH"))])]
+     (section-item "Avatar"
+                   [:div.flex.flex-row.space-x-6.items-center
+                    (ui/avatar
+                     (ui/avatar-image {:src "https://avatars.githubusercontent.com/u/63385289?s=200&v=4"})
+                     (ui/avatar-fallback "L"))
+                    (ui/avatar
+                     (ui/avatar-fallback "CH"))])]
 
      ;; Badge
-     (section-item "Badge"
-       [:div.flex.flex-row.flex-wrap.gap-2
-        (ui/badge "Default")
-        (ui/badge {:variant :outline} "Outline")
-        (ui/badge {:variant :secondary} "Secondary")
-        (ui/badge {:variant :destructive} "Destructive")
-        (ui/badge {:class "primary-yellow"} "Custom (.primary-yellow)")])
-
-     [:div.grid.sm:grid-cols-3.sm:gap-8
+    (section-item "Badge"
+                  [:div.flex.flex-row.flex-wrap.gap-2
+                   (ui/badge "Default")
+                   (ui/badge {:variant :outline} "Outline")
+                   (ui/badge {:variant :secondary} "Secondary")
+                   (ui/badge {:variant :destructive} "Destructive")
+                   (ui/badge {:class "primary-yellow"} "Custom (.primary-yellow)")])
+
+    [:div.grid.sm:grid-cols-3.sm:gap-8
       ;; Dropdown
-      (section-item "Dropdown"
-        (ui/dropdown-menu
-          (ui/tooltip
-            (ui/tooltip-trigger
-              (ui/dropdown-menu-trigger
-                {:as-child true}
-                (ui/button {:variant :outline}
-                  (ui/tabler-icon "list") "Open dropdown menu")))
-            (ui/tooltip-content "test hide?"))
-
-          (sample-dropdown-menu-content)))
+     (section-item "Dropdown"
+                   (ui/dropdown-menu
+                    (ui/tooltip
+                     (ui/tooltip-trigger
+                      (ui/dropdown-menu-trigger
+                       {:as-child true}
+                       (ui/button {:variant :outline}
+                                  (ui/tabler-icon "list") "Open dropdown menu")))
+                     (ui/tooltip-content "test hide?"))
+
+                    (sample-dropdown-menu-content)))
 
       ;; Context menu
-      [:div.col-span-2
-       (section-item "Context Menu"
-         (sample-context-menu-content))]]
+     [:div.col-span-2
+      (section-item "Context Menu"
+                    (sample-context-menu-content))]]
+
+    (section-item "Tabs" (sample-tabs))
 
      ;; Dialog
-     (section-item "Dialog"
-       [:div.flex.flex-row.flex-wrap.gap-2
-        (sample-dialog-basic)
-        (ui/button
-          {:on-click #(dialog-core/open! "a modal dialog from `open!`" {:title "Title"})}
-          "Imperative API: open!")
-
-        (ui/button
-          {:class "primary-yellow"
-           :on-click (fn []
-                       (-> (dialog-core/alert!
-                             "a alert dialog from `alert!`"
-                             {:title [:div.flex.flex-row.space-x-2.items-center
-                                      (ui/tabler-icon "alert-triangle" {:size 18})
-                                      [:span "Alert"]]})
-                         (p/then #(js/console.log "=> alert (promise): " %))))}
-          "Imperative API: alert!")
-
-        (ui/button
-          {:class "primary-green"
-           :on-click (fn []
-                       (-> (dialog-core/confirm!
-                             "a alert dialog from `confirm!`"
-                             {:title [:div.flex.flex-row.space-x-2.items-center
-                                      (ui/tabler-icon "alert-triangle" {:size 18})
-                                      [:span "Confirm"]]})
-                         (p/then #(js/console.log "=> confirm (promise): " %))
-                         (p/catch #(js/console.log "=> confirm (promise): " %))))}
-          "Imperative API: confirm!")])
+    (section-item "Dialog"
+                  [:div.flex.flex-row.flex-wrap.gap-2
+                   (sample-dialog-basic)
+                   (ui/button
+                    {:on-click #(dialog-core/open! "a modal dialog from `open!`" {:title "Title"})}
+                    "Imperative API: open!")
+
+                   (ui/button
+                    {:class "primary-yellow"
+                     :on-click (fn []
+                                 (-> (dialog-core/alert!
+                                      "a alert dialog from `alert!`"
+                                      {:title [:div.flex.flex-row.space-x-2.items-center
+                                               (ui/tabler-icon "alert-triangle" {:size 18})
+                                               [:span "Alert"]]})
+                                     (p/then #(js/console.log "=> alert (promise): " %))))}
+                    "Imperative API: alert!")
+
+                   (ui/button
+                    {:class "primary-green"
+                     :on-click (fn []
+                                 (-> (dialog-core/confirm!
+                                      "a alert dialog from `confirm!`"
+                                      {:title [:div.flex.flex-row.space-x-2.items-center
+                                               (ui/tabler-icon "alert-triangle" {:size 18})
+                                               [:span "Confirm"]]})
+                                     (p/then #(js/console.log "=> confirm (promise): " %))
+                                     (p/catch #(js/console.log "=> confirm (promise): " %))))}
+                    "Imperative API: confirm!")])
 
      ;; Alert
-     (section-item "Alert"
-       [:<>
-        (ui/alert
-          {:class "text-orange-rx-09 border-orange-rx-07-alpha mb-4"}
-          (ui/tabler-icon "brand-soundcloud")
-          (ui/alert-title "Title is SoundCloud")
-          (ui/alert-description
-            "content: radix colors for Logseq"))
-        (ui/alert
-          (ui/tabler-icon "brand-github")
-          (ui/alert-title "GitHub")
-          (ui/alert-description
-            "content: radix colors for Logseq"))])
+    (section-item "Alert"
+                  [:<>
+                   (ui/alert
+                    {:class "text-orange-rx-09 border-orange-rx-07-alpha mb-4"}
+                    (ui/tabler-icon "brand-soundcloud")
+                    (ui/alert-title "Title is SoundCloud")
+                    (ui/alert-description
+                     "content: radix colors for Logseq"))
+                   (ui/alert
+                    (ui/tabler-icon "brand-github")
+                    (ui/alert-title "GitHub")
+                    (ui/alert-description
+                     "content: radix colors for Logseq"))])
 
      ;; Slider
-     [:div.grid.sm:grid-cols-8.gap-4
-      [:div.col-span-4.mr-6
-       (section-item "Slider" (ui/slider))]
-      [:div.col-span-1
-       (section-item "Switch"
-         (ui/switch {:size :sm :class "relative top-[-8px]"}))]
-      [:div.col-span-3.pl-4.pr-2
-       (section-item "Select"
-         (ui/select
-           {:on-value-change (fn [v] (ui/toast! v :info))}
+    [:div.grid.sm:grid-cols-8.gap-4
+     [:div.col-span-4.mr-6
+      (section-item "Slider" (ui/slider))]
+     [:div.col-span-1
+      (section-item "Switch"
+                    (ui/switch {:size :sm :class "relative top-[-8px]"}))]
+     [:div.col-span-3.pl-4.pr-2
+      (section-item "Select"
+                    (ui/select
+                     {:on-value-change (fn [v] (ui/toast! v :info))}
            ;; trigger
-           (ui/select-trigger
-             (ui/select-value {:placeholder "Select a fruit"}))
+                     (ui/select-trigger
+                      (ui/select-value {:placeholder "Select a fruit"}))
            ;; content
-           (ui/select-content
-             (ui/select-group
-               (ui/select-label "Fruits")
-               (ui/select-item {:value "apple"} "Apple")
-               (ui/select-item {:value "pear"} "Pear")
-               (ui/select-item {:value "grapes"} "Grapes")
-
-               ))))]]
-
-     ;; Form
-     (section-item "Form"
-       [:<>
-        (sample-form-basic)])
+                     (ui/select-content
+                      (ui/select-group
+                       (ui/select-label "Fruits")
+                       (ui/select-item {:value "apple"} "Apple")
+                       (ui/select-item {:value "pear"} "Pear")
+                       (ui/select-item {:value "grapes"} "Grapes")))))]]
+
+;; Form
+    (section-item "Form"
+                  [:<>
+                   (sample-form-basic)])
 
      ;; Card
-     [:div.grid.sm:grid-cols-2.sm:gap-8
-      (section-item "Card"
-        (ui/card
-          (ui/card-header
-            (ui/card-title "Title")
-            (ui/card-description "Description"))
-          (ui/card-content "This is content")
-          (ui/card-footer "Footer")))
-
-      (section-item "Skeleton"
-        (ui/card
-          (ui/card-header
-            (ui/card-title
-              (ui/skeleton {:class "h-4 w-1/2"}))
-            (ui/card-description
-              (ui/skeleton {:class "h-2 w-full"})))
-          (ui/card-content
-            (ui/skeleton {:class "h-3 mb-1"})
-            (ui/skeleton {:class "h-3 mb-1"})
-            (ui/skeleton {:class "h-3 w-2/3"}))
-
-          (ui/card-footer
-            (ui/skeleton {:class "h-4 w-full mb-2"}))))]
+    [:div.grid.sm:grid-cols-2.sm:gap-8
+     (section-item "Card"
+                   (ui/card
+                    (ui/card-header
+                     (ui/card-title "Title")
+                     (ui/card-description "Description"))
+                    (ui/card-content "This is content")
+                    (ui/card-footer "Footer")))
+
+     (section-item "Skeleton"
+                   (ui/card
+                    (ui/card-header
+                     (ui/card-title
+                      (ui/skeleton {:class "h-4 w-1/2"}))
+                     (ui/card-description
+                      (ui/skeleton {:class "h-2 w-full"})))
+                    (ui/card-content
+                     (ui/skeleton {:class "h-3 mb-1"})
+                     (ui/skeleton {:class "h-3 mb-1"})
+                     (ui/skeleton {:class "h-3 w-2/3"}))
+
+                    (ui/card-footer
+                     (ui/skeleton {:class "h-4 w-full mb-2"}))))]
 
      ;; Calendar
-     [:div.grid.sm:grid-cols-2.sm:gap-8
-      (section-item "Calendar"
-        (ui/card
-          {:class "inline-flex"}
-          (ui/calendar {:on-day-click #(ui/toast! (.toString %) :success)})))
-      (section-item "Date Picker"
-        (sample-date-picker))]
-
-     [:hr.mb-80]]))
+    [:div.grid.sm:grid-cols-2.sm:gap-8
+     (section-item "Calendar"
+                   (ui/card
+                    {:class "inline-flex"}
+                    (ui/calendar {:on-day-click #(ui/toast! (.toString %) :success)})))
+     (section-item "Date Picker"
+                   (sample-date-picker))]
 
+    [:hr.mb-80]]))
 
 (defn- get-head-container
   []
@@ -499,44 +512,44 @@
 
   (let [el-ref (rum/use-ref nil)]
     (rum/use-effect!
-      (fn []
-        (let [^js container (get-main-scroll-container)
-              ^js el (rum/deref el-ref)
-              ^js cls (.-classList el)
-              *ticking? (volatile! false)
-              el-top (-> el (.getBoundingClientRect) (.-top))
-              head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
-              translate (fn [offset]
-                          (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)"))
-                          (if (zero? offset)
-                            (.remove cls "translated")
-                            (.add cls "translated")))
-              *last-offset (volatile! 0)
-              handle (fn []
-                       (let [scroll-top (js/parseInt (.-scrollTop container))
-                             offset (if (> (+ scroll-top head-top) el-top)
-                                      (+ (- scroll-top el-top) head-top 1) 0)
-                             offset (js/parseInt offset)
-                             last-offset @*last-offset]
-                         (if (and (not (zero? last-offset))
-                               (not= offset last-offset))
-                           (let [dir (if (neg? (- offset last-offset)) -1 1)]
-                             (loop [offset' (+ last-offset dir)]
-                               (translate offset')
-                               (if (and (not= offset offset')
-                                     (< (abs (- offset offset')) 100))
-                                 (recur (+ offset' dir))
-                                 (translate offset))))
-                           (translate offset))
-                         (vreset! *last-offset offset)))
-              handler (fn [^js e]
-                        (when (not @*ticking?)
-                          (js/window.requestAnimationFrame
-                            #(do (handle) (vreset! *ticking? false)))
-                          (vreset! *ticking? true)))]
-          (.addEventListener container "scroll" handler)
-          #(.removeEventListener container "scroll" handler)))
-      [])
+     (fn []
+       (let [^js container (get-main-scroll-container)
+             ^js el (rum/deref el-ref)
+             ^js cls (.-classList el)
+             *ticking? (volatile! false)
+             el-top (-> el (.getBoundingClientRect) (.-top))
+             head-top (-> (get-head-container) (js/getComputedStyle) (.-height) (js/parseInt))
+             translate (fn [offset]
+                         (set! (. (.-style el) -transform) (str "translate3d(0, " offset "px , 0)"))
+                         (if (zero? offset)
+                           (.remove cls "translated")
+                           (.add cls "translated")))
+             *last-offset (volatile! 0)
+             handle (fn []
+                      (let [scroll-top (js/parseInt (.-scrollTop container))
+                            offset (if (> (+ scroll-top head-top) el-top)
+                                     (+ (- scroll-top el-top) head-top 1) 0)
+                            offset (js/parseInt offset)
+                            last-offset @*last-offset]
+                        (if (and (not (zero? last-offset))
+                                 (not= offset last-offset))
+                          (let [dir (if (neg? (- offset last-offset)) -1 1)]
+                            (loop [offset' (+ last-offset dir)]
+                              (translate offset')
+                              (if (and (not= offset offset')
+                                       (< (abs (- offset offset')) 100))
+                                (recur (+ offset' dir))
+                                (translate offset))))
+                          (translate offset))
+                        (vreset! *last-offset offset)))
+             handler (fn [^js e]
+                       (when (not @*ticking?)
+                         (js/window.requestAnimationFrame
+                          #(do (handle) (vreset! *ticking? false)))
+                         (vreset! *ticking? true)))]
+         (.addEventListener container "scroll" handler)
+         #(.removeEventListener container "scroll" handler)))
+     [])
 
     [:div.charlie-table
      [:div.charlie-table-header

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

@@ -106,6 +106,12 @@
 (def context-menu-sub-trigger (util/lsui-wrap "ContextMenuSubTrigger"))
 (def context-menu-radio-group (util/lsui-wrap "ContextMenuRadioGroup"))
 
+;; tabs
+(def tabs (util/lsui-wrap "Tabs"))
+(def tabs-list (util/lsui-wrap "TabsList"))
+(def tabs-trigger (util/lsui-wrap "TabsTrigger"))
+(def tabs-content (util/lsui-wrap "TabsContent"))
+
 (def dialog dialog-core/dialog)
 (def dialog-portal dialog-core/dialog-portal)
 (def dialog-overlay dialog-core/dialog-overlay)

+ 53 - 0
packages/ui/@/components/ui/tabs.tsx

@@ -0,0 +1,53 @@
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+  React.ElementRef<typeof TabsPrimitive.List>,
+  React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
+>(({ className, ...props }, ref) => (
+  <TabsPrimitive.List
+    ref={ref}
+    className={cn(
+      "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
+      className
+    )}
+    {...props}
+  />
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+  React.ElementRef<typeof TabsPrimitive.Trigger>,
+  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
+>(({ className, ...props }, ref) => (
+  <TabsPrimitive.Trigger
+    ref={ref}
+    className={cn(
+      "inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
+      className
+    )}
+    {...props}
+  />
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+  React.ElementRef<typeof TabsPrimitive.Content>,
+  React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
+>(({ className, ...props }, ref) => (
+  <TabsPrimitive.Content
+    ref={ref}
+    className={cn(
+      "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
+      className
+    )}
+    {...props}
+  />
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }

+ 1 - 0
packages/ui/package.json

@@ -30,6 +30,7 @@
     "@radix-ui/react-slider": "^1.1.2",
     "@radix-ui/react-slot": "^1.0.2",
     "@radix-ui/react-switch": "^1.0.3",
+    "@radix-ui/react-tabs": "^1.1.1",
     "@radix-ui/react-toast": "^1.1.5",
     "@radix-ui/react-toggle": "^1.0.3",
     "@radix-ui/react-toggle-group": "^1.0.4",

+ 5 - 3
packages/ui/src/ui.ts

@@ -91,6 +91,7 @@ import { Toggle } from '@/components/ui/toggle'
 import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'
 import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
 import * as uniqolor from 'uniqolor'
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
 
 declare global {
   var LSUI: any
@@ -98,7 +99,7 @@ declare global {
 }
 
 const shadui = {
-  Link, Button, 
+  Link, Button,
   Slider, SliderTrack, SliderRange, SliderThumb,
   DropdownMenu,
   DropdownMenuContent,
@@ -184,7 +185,8 @@ const shadui = {
   Tooltip, TooltipTrigger, TooltipArrow,
   TooltipContent, TooltipProvider, TooltipPortal,
   Toggle, ToggleGroup, ToggleGroupItem,
-  Avatar, AvatarImage, AvatarFallback
+  Avatar, AvatarImage, AvatarFallback,
+  Tabs, TabsContent, TabsList, TabsTrigger
 }
 
 function setupGlobals() {
@@ -203,4 +205,4 @@ setupGlobals()
 
 export {
   setupGlobals
-}
+}

+ 138 - 6
packages/ui/yarn.lock

@@ -2197,6 +2197,11 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2"
+  integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==
+
 "@radix-ui/react-alert-dialog@^1.0.5":
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.0.5.tgz#70dd529cbf1e4bff386814d3776901fcaa131b8c"
@@ -2255,6 +2260,16 @@
     "@radix-ui/react-primitive" "1.0.3"
     "@radix-ui/react-slot" "1.0.2"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed"
+  integrity sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==
+  dependencies:
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-context" "1.1.0"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-slot" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz#37595b1f16ec7f228d698590e78eeed18ff218ae"
@@ -2269,6 +2284,11 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74"
+  integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==
+
 "@radix-ui/react-context-menu@^2.1.5":
   version "2.1.5"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-context-menu/-/react-context-menu-2.1.5.tgz#1bdbd72761439f9166f75dc4598f276265785c83"
@@ -2296,6 +2316,16 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8"
+  integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==
+
+"@radix-ui/[email protected]":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a"
+  integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-dialog/-/react-dialog-1.0.0.tgz#997e97cb183bc90bd888b26b8e23a355ac9fe5f0"
@@ -2345,6 +2375,11 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc"
+  integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.0.tgz#35b7826fa262fd84370faef310e627161dffa76b"
@@ -2455,6 +2490,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-use-layout-effect" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed"
+  integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==
+  dependencies:
+    "@radix-ui/react-use-layout-effect" "1.1.0"
+
 "@radix-ui/react-label@^2.0.2":
   version "2.0.2"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-label/-/react-label-2.0.2.tgz#9c72f1d334aac996fdc27b48a8bdddd82108fb6d"
@@ -2586,6 +2628,14 @@
     "@radix-ui/react-compose-refs" "1.0.1"
     "@radix-ui/react-use-layout-effect" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1"
+  integrity sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==
+  dependencies:
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-use-layout-effect" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.0.tgz#376cd72b0fcd5e0e04d252ed33eb1b1f025af2b0"
@@ -2602,6 +2652,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-slot" "1.0.2"
 
+"@radix-ui/[email protected]":
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884"
+  integrity sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==
+  dependencies:
+    "@radix-ui/react-slot" "1.1.0"
+
 "@radix-ui/react-radio-group@^1.1.3":
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-radio-group/-/react-radio-group-1.1.3.tgz#3197f5dcce143bcbf961471bf89320735c0212d3"
@@ -2635,6 +2692,21 @@
     "@radix-ui/react-use-callback-ref" "1.0.1"
     "@radix-ui/react-use-controllable-state" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz#b30c59daf7e714c748805bfe11c76f96caaac35e"
+  integrity sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==
+  dependencies:
+    "@radix-ui/primitive" "1.1.0"
+    "@radix-ui/react-collection" "1.1.0"
+    "@radix-ui/react-compose-refs" "1.1.0"
+    "@radix-ui/react-context" "1.1.0"
+    "@radix-ui/react-direction" "1.1.0"
+    "@radix-ui/react-id" "1.1.0"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-use-callback-ref" "1.1.0"
+    "@radix-ui/react-use-controllable-state" "1.1.0"
+
 "@radix-ui/react-select@^1.2.2":
   version "1.2.2"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-1.2.2.tgz#caa981fa0d672cf3c1b2a5240135524e69b32181"
@@ -2733,6 +2805,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-compose-refs" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84"
+  integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==
+  dependencies:
+    "@radix-ui/react-compose-refs" "1.1.0"
+
 "@radix-ui/react-switch@^1.0.3":
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-switch/-/react-switch-1.0.3.tgz#6119f16656a9eafb4424c600fdb36efa5ec5837e"
@@ -2747,6 +2826,20 @@
     "@radix-ui/react-use-previous" "1.0.1"
     "@radix-ui/react-use-size" "1.0.1"
 
+"@radix-ui/react-tabs@^1.1.1":
+  version "1.1.1"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-tabs/-/react-tabs-1.1.1.tgz#698bd97923f6bcd629738198a73beebcc4c88b30"
+  integrity sha512-3GBUDmP2DvzmtYLMsHmpA1GtR46ZDZ+OreXM/N+kkQJOPIgytFWWTfDQmBQKBvaFS0Vno0FktdbVzN28KGrMdw==
+  dependencies:
+    "@radix-ui/primitive" "1.1.0"
+    "@radix-ui/react-context" "1.1.1"
+    "@radix-ui/react-direction" "1.1.0"
+    "@radix-ui/react-id" "1.1.0"
+    "@radix-ui/react-presence" "1.1.1"
+    "@radix-ui/react-primitive" "2.0.0"
+    "@radix-ui/react-roving-focus" "1.1.0"
+    "@radix-ui/react-use-controllable-state" "1.1.0"
+
 "@radix-ui/react-toast@^1.1.5":
   version "1.1.5"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-toast/-/react-toast-1.1.5.tgz#f5788761c0142a5ae9eb97f0051fd3c48106d9e6"
@@ -2837,6 +2930,11 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1"
+  integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.0.tgz#a64deaafbbc52d5d407afaa22d493d687c538b7f"
@@ -2853,6 +2951,13 @@
     "@babel/runtime" "^7.13.10"
     "@radix-ui/react-use-callback-ref" "1.0.1"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0"
+  integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==
+  dependencies:
+    "@radix-ui/react-use-callback-ref" "1.1.0"
+
 "@radix-ui/[email protected]":
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.0.tgz#aef375db4736b9de38a5a679f6f49b45a060e5d1"
@@ -2883,6 +2988,11 @@
   dependencies:
     "@babel/runtime" "^7.13.10"
 
+"@radix-ui/[email protected]":
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27"
+  integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==
+
 "@radix-ui/[email protected]":
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66"
@@ -8751,8 +8861,7 @@ stream-shift@^1.0.0:
   resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
   integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
 
-"string-width-cjs@npm:string-width@^4.2.0", string-width@^4, string-width@^4.1.0, string-width@^4.2.0, string-width@^5.0.1, string-width@^5.1.2:
-  name string-width-cjs
+"string-width-cjs@npm:string-width@^4.2.0":
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
   integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@@ -8770,6 +8879,15 @@ [email protected]:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.0"
 
+string-width@^4, string-width@^4.1.0, string-width@^4.2.0, string-width@^5.0.1, string-width@^5.1.2:
+  version "4.2.3"
+  resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+  integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+  dependencies:
+    emoji-regex "^8.0.0"
+    is-fullwidth-code-point "^3.0.0"
+    strip-ansi "^6.0.1"
+
 string_decoder@^1.1.1:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -8784,8 +8902,14 @@ string_decoder@~1.1.1:
   dependencies:
     safe-buffer "~5.1.0"
 
-"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1:
-  name strip-ansi-cjs
+"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
+  version "6.0.1"
+  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+  integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+  dependencies:
+    ansi-regex "^5.0.1"
+
+strip-ansi@^6, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1:
   version "6.0.1"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
   integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@@ -9445,8 +9569,16 @@ wordwrap@^1.0.0:
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
   integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==
 
-"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
-  name wrap-ansi-cjs
+"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
+  version "7.0.0"
+  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+  integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+  dependencies:
+    ansi-styles "^4.0.0"
+    string-width "^4.1.0"
+    strip-ansi "^6.0.0"
+
+wrap-ansi@^7.0.0:
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
   integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==